import { Func, LocalLanguage, ScriptOptions, ScriptOptionUpdateParam } from "../../common/types";
import { Diagnostic } from "../diagnostic";
import { NodeFactory } from "./factory";

export interface Predicate<T> {
    (arg: T, ...args: any[]): boolean;
}

export enum CharacterCodes {
    backSpace = 8,
    tab = 9,                //  '\t'
    lineFeed = 10,          //  '\n'
    carriageReturn = 13,    //  '\r'
    shiftOut = 14,
    space = 32,
    exclamation = 33,        //  '!'
    quotation = 34,          //  '"'
    numberSign = 35,         //  '#'
    dollarSign = 36,         //  '$'
    percentSign = 37,        //  '%'
    ampersand = 38,          //  '&'
    apostrophe = 39,         //  '''
    openParenthesis = 40,    //  '('
    closeParenthesis = 41,   //  ')'
    asterisk = 42,           //  '*'
    plus = 43,               //  '+'
    comma = 44,              //  ','
    minus = 45,              //  '-'
    dot = 46,                //  '.'
    slash = 47,              //  '/'
    digit0 = 48,             //  '0'
    digit1 = 49,             //  '1'
    digit2 = 50,             //  '2'
    digit3 = 51,             //  '3'
    digit4 = 52,             //  '4'
    digit5 = 53,             //  '5'
    digit6 = 54,             //  '6'
    digit7 = 55,             //  '7'
    digit8 = 56,             //  '8'
    digit9 = 57,             //  '9'
    colon = 58,              //  ':'
    semicolon = 59,          //  ';'
    lessThan = 60,           //  '<'
    equalsTo = 61,           //  '='
    greaterThan = 62,        //  '>'
    questionMark = 63,       //  '?'
    atSign = 64,             //  '@'
    uppercaseA = 65,         //  'A'
    uppercaseB = 66,         //  'B'
    uppercaseC = 67,         //  'C'
    uppercaseD = 68,         //  'D'
    uppercaseE = 69,         //  'E'
    uppercaseF = 70,         //  'F'
    uppercaseG = 71,         //  'G'
    uppercaseH = 72,         //  'H'
    uppercaseI = 73,         //  'I'
    uppercaseJ = 74,         //  'J'
    uppercaseK = 75,         //  'K'
    uppercaseL = 76,         //  'L'
    uppercaseM = 77,         //  'M'
    uppercaseN = 78,         //  'N'
    uppercaseO = 79,         //  'O'
    uppercaseP = 80,         //  'P'
    uppercaseQ = 81,         //  'Q'
    uppercaseR = 82,         //  'R'
    uppercaseS = 83,         //  'S'
    uppercaseT = 84,         //  'T'
    uppercaseU = 85,         //  'U'
    uppercaseV = 86,         //  'V'
    uppercaseW = 87,         //  'W'
    uppercaseX = 88,         //  'X'
    uppercaseY = 89,         //  'Y'
    uppercaseZ = 90,         //  'Z'
    openSquareBracket = 91,  //  '['
    backslash = 92,          //  '\'
    closeSquareBracket = 93, //  ']'
    caret = 94,              //  '^'
    underscore = 95,         //  '_'
    graveAccent = 96,        //  '`'
    lowercaseA = 97,         //  'a'
    lowercaseB = 98,         //  'b'
    lowercaseC = 99,         //  'c'
    lowercaseD = 100,        //  'd'
    lowercaseE = 101,        //  'e'
    lowercaseF = 102,        //  'f'
    lowercaseG = 103,        //  'g'
    lowercaseH = 104,        //  'h'
    lowercaseI = 105,        //  'i'
    lowercaseJ = 106,        //  'j'
    lowercaseK = 107,        //  'k'
    lowercaseL = 108,        //  'l'
    lowercaseM = 109,        //  'm'
    lowercaseN = 110,        //  'n'
    lowercaseO = 111,        //  'o'
    lowercaseP = 112,        //  'p'
    lowercaseQ = 113,        //  'q'
    lowercaseR = 114,        //  'r'
    lowercaseS = 115,        //  's'
    lowercaseT = 116,        //  't'
    lowercaseU = 117,        //  'u'
    lowercaseV = 118,        //  'v'
    lowercaseW = 119,        //  'w'
    lowercaseX = 120,        //  'x'
    lowercaseY = 121,        //  'y'
    lowercaseZ = 122,        //  'z'
    openCurlyBrace = 123,    //  '{'
    verticalBar = 124,       //  '|'
    closeCurlyBrace = 125,   //  '}'
    tilde = 126,             //  '~'
    nonBreakingSpace = 160,
    oghamSpaceMark = 5760,   // ' '
    lineSeparator = 8232,
    paragraphSeparator = 8233,
    maxAsciiCharacter = 127,
}

export enum LexContext {
    mrs,
    declare,
    dms,
    metadata,
    pp,
}

export interface State {
    lexContext: LexContext;
    pos: number;
    startPos: number;
    length: number;
    text: string;
    token: SyntaxKind;
    tokenStart: number;
    tokenText: string;
    tokenValue: string;
    tokenFlag: TokenFlags;
    //
    atFirstLine: boolean;
    option: OptionFlag;
    optionNode?: OptionStatement;
}

export interface LexToken extends ReadonlyTextRange {
    _lexTokenBrand: any;
    readonly url: string;
    readonly kind: SyntaxKind;
    readonly text: string;
}

export enum MacroExpansionKind {
    element,
    macro,
    parameter,
}

export interface MacroExpansion extends LexToken {
    _macroExpansionBrand: any;
    readonly expansionKind: MacroExpansionKind;
    readonly relative?: LexToken; // 此宏扩展可能指向的宏参数或者其他宏
}

export interface ScriptFile {
    _scriptFileBrand: any;
    readonly path: string;
    readonly content: string;
    readonly tokens: LexToken[];
    readonly statements: Statement[];
    readonly symbolTable: Map<string, Symbol>;
    /* @internal */ readonly decl: Map<string, Declaration>;
}

export enum ScannerContextType {
    normal,
    metadata,
    preprocessor,
}

export interface ScannerState {
    text: string;
    pos: number;
    end: number;
    startPos: number;
    tokenStart: number;
    token: SyntaxKind;
    tokenValue: string;
    tokenFlag: TokenFlags;
    atFirstLine: boolean;
    context: ScannerContextType;
}

/**
 * 脚本文本扫描器，由于SPSS DataCollection Script Basic脚本基于VBS语法，语句没有结束符号，并且多个句子不允许放在同一行，
 * 所以判断语句结束依赖于对于换行的判断。此外，脚本还有行连接符'\'和'_'，使用这两个符号后，前后两行应当视为同一行，应当跳过连接符并连接前后的内容，
 * 例如：
 *
 * | 原脚本    |     连接后  |
 * | :------  |  :------   |
 * | a_       |            |
 * | cdeft    |     acdeft |
 *
 * 所以，对于任何识别符(identifier)的验证都需要在链接字符之后。
 *
 * 需要额外注意的是，对于参数宏，换行不需要换行符链接符，链接字符需要当作一般字符处理。
 *
 */
export interface Scanner {
    scan(): SyntaxKind;
    getToken(): SyntaxKind;
    getTokenText(): string;
    getTokenText(start: number, end?: number): string;
    getStartPos(): number;
    getTextPos(): number;
    getTokenStart(): number;
    lookAhead<T>(callback: () => T): T;
    tryScan<T>(callback: () => T): T;
    hasPrecedingLinebreak(): boolean;
    setContext(type: ScannerContextType): void;
    saveState(): ScannerState;
    recoveryState(state: ScannerState): void;
    atBegining(): boolean;
    atFirstLine(): boolean;
    getTokenValue(): string;
    getTokenFlag(): TokenFlags;
    getComments(): Comment[];
    setText(newText: string | undefined, start?: number | undefined, length?: number | undefined): void;
    onError(onErrorCallback?: Func<Diagnostic, void>): void;
    isIdentifier(): boolean;
    isPreprocessorOperator(): boolean;
    isReservedWord(): boolean;
    isDelcareReservedWord(): boolean;
    isTrueLiteral(): boolean;
    isFalseLiteral(): boolean;
    isNullLiteral(): boolean;
    scanRange<T>(start: number, length: number, callback: () => T): T;
    scanToLinefeedAsString(): SyntaxKind.stringLiteral;
    reScanInvalidIdentifier(): SyntaxKind;
}

// Parsing Context

export const enum ParserContext {
    none                    = 0,
    name                    = 1 << 0,
    paren                   = 1 << 1,
    categorical             = 1 << 2,
    blockStat               = 1 << 3,
    withStat                = 1 << 4,
    doStat                  = 1 << 5,
    whileStat               = 1 << 6,
    ifStat                  = 1 << 7,
    forStat                 = 1 << 8,
    functionDecl            = 1 << 9,
    subDecl                 = 1 << 10,
    selectClauses           = 1 << 11,
    selectStat              = 1 << 12,
    classSignature          = 1 << 13,
    interfaceSignature      = 1 << 14,
    moduleSignature         = 1 << 15,
    parameters              = 1 << 16,
    preprocessor            = 1 << 17,
    section                 = 1 << 18,
    eventSection            = 1 << 19,
    metadataSection         = 1 << 20,
    routingSection          = 1 << 21,
    dmsSections             = eventSection | routingSection,
}

export interface ParserContextStateNode {
    context: ParserContext;
    node?: Statement | Expression;
}

export interface ParserContextState {
    frozen: boolean;
    currentFrozenContext: ParserContext;
    frozenContexts: ParserContextStateNode[];
    currentContext: ParserContext;
    contexts: ParserContextStateNode[];
}

export interface ParserContextHandler {
    current(): ParserContext;
    currentContextNode(): Statement | Expression | undefined;
    once<T>(context: ParserContext, callback: () => T): T;
    push(context: ParserContext, node?: Statement | Expression): void;
    remove(context: ParserContext): void;
    getContextNode(context: ParserContext): Statement | Expression | undefined;
    test(context: ParserContext): boolean;
    count(context: ParserContext): number;
    clear(): void;
    freeze(): void;
    unfreeze(): void;
    saveState(): ParserContextState;
    recoveryState(savedState: ParserContextState): void;
}

export interface ResolveMacroParams {
    node: Identifier;
    start: number;
    end: number;
}

export interface MacroExpandMapping extends ReadonlyTextRange {
    inCurrentFile: boolean;
}

export interface MacroExpandToken {
    readonly token: Node;
    readonly sourceMapping: MacroExpandMapping;
}

export interface MacroExpandResult {
    readonly nodes: readonly MacroExpandToken[];
    readonly failure: boolean;
}

export interface ParserState {
    scannerState: ScannerState;
    contextState: ParserContextState;
    parserDiagnostics: Diagnostic[];
    sourceFileName: string;
    sourceFileText: string;
    fileFlag: FileKind;
    nodeCount: number;
    preprocessorNodeCount: number;
    identifierCount: number;
    optionSettingNode: OptionStatement | undefined;
    optionFlag: OptionFlag;
    currentToken: SyntaxKind;
    originIdentifiers: Map<string, string>;
    signatures: Signature<SignatureKind>[];
    declarations: FunctionDeclaration[];
    identifiers: Identifier[];
    globalLocations: LineLocation[];
    references: SourceFile[];
    includeNodes: PPInclude[];
    sectionNodes: Section[];
    comments: Comment[];
    resolveMacroInvokeNodeStack: Func<ResolveMacroParams, void>[];
    macroNodes: Identifier[];
    macroExpandNodes: MacroExpandToken[];
    macroExpandIndex: number;
}

export const enum GenerateFileExtens {
    declare = ".d.mrs",
    metadata = ".m.dms",
    routing = ".r.dms",
};

export enum FileKind {
    none,
    dms,
    mrs,
    bat,
    ini,
    mdd,
    declare,
    metadata,
    routing,
}

export enum CommentType {
    none            = 0,
    ignoreTypeError = 1 << 0,
}

export interface LocationBlockNode {
    /* @internal */ readonly locations: {
        nodes: LineLocation[],
        symbols: Map<string, Symbol>,
    };
}

export interface SourceFile extends Declaration, LocationBlockNode {
    readonly kind: SyntaxKind.sourceFile;
    readonly fileName: string;             // 文件名，包含文件路径
    readonly fileKind: FileKind;
    readonly references: readonly SourceFile[]; // 文件中直接引用的所有文件，Event内引用的文件不包含在里面
    readonly sections: readonly Section[];
    readonly includes: readonly PPInclude[];
    readonly statements: NodeArray<Statement>;
    readonly metadata?: MDMDocument;
    readonly eof: Token<SyntaxKind.eof>;
    readonly diagnostics: Diagnostic[];
    readonly locals: SymbolTable;
    readonly macros: ReadonlyMap<string, Symbol>;
    /* @internal */ lineMap: readonly number[];
    /* @internal */ declarations: FunctionDeclaration[];
    /* @internal */ signatures: Signature<SignatureKind>[];
    /* @internal */ commentFlag: CommentType;
    /* @internal */ comments: readonly Comment[];
    /* @internal */ identifiers: Identifier[];
    /* @internal */ macroNodes: NodeArray<ParameterMacroCallExpr | Identifier>;

    update(newText: string): void;
    getLineAndCharacterOfLastLineEndPosition(position: number): TextPosition;
    getLineAndCharactorOfPosition(position: number): TextPosition;
    getPositionOfLineAndCharacter(line: number, character: number): number;
    getCommentAtPosition(position: number): Comment | undefined;

}

export interface TextPosition {
    line: number;
    character: number;
}

export interface TextRange {
    start: number;
    end: number;
}

export interface TextChangeRange {
    start: number;
    length: number;
    newLength: number;
}

export interface ReadonlyTextRange {
    readonly start: number;
    readonly end: number;
}

export interface TextSpan {
    start: number;
    length: number;
}

export type TextChange = {
    span: TextSpan;
    text: string;
};

export const enum SymbolKind {
    undefined              = 1 << 0,
    macroVariable          = 1 << 1,
    macroFunction          = 1 << 2,
    variable               = 1 << 3,
    parameter              = 1 << 4,
    constant               = 1 << 5,
    array                  = 1 << 6,
    function               = 1 << 7,
    property               = 1 << 8,
    method                 = 1 << 9,
    literal                = 1 << 10,
    // declaration
    class                  = 1 << 11,
    interface              = 1 << 12,
    module                 = 1 << 13,
    enum                   = 1 << 14,
    enumMember             = 1 << 15,
    // metadata
    metadata               = 1 << 16,
    // section
    section                = 1 << 17,
    sectionMember          = 1 << 18,
    // location
    location               = 1 << 19,
    // source file
    sourceFile             = 1 << 20,

    localDeclarations      = SymbolKind.variable | SymbolKind.array | SymbolKind.constant,
    reference              = SymbolKind.interface | SymbolKind.class | SymbolKind.module,
    functionLike           = SymbolKind.function | method | macroFunction,
    objectMember           = SymbolKind.function | property,
    allowImplicitDeclare   = variable | array,

    type                   = SymbolKind.interface | SymbolKind.class | SymbolKind.enum,
    macro                  = macroFunction | macroVariable,
}

export enum CheckFlag {
    none               = 0,
    instantiated       = 1 << 0,
    readonly           = 1 << 1,
    writeonly          = 1 << 2,
    optional           = 1 << 3,
    paramArray         = 1 << 4,
    numberIndex        = 1 << 5,
    stringIndex        = 1 << 6,
    objectIterator     = 1 << 7,

    optionalLike   = optional | paramArray,
    indexable      = numberIndex | stringIndex | objectIterator,
}

export enum SymbolMDMKind {
    none,
    areaName,
    property,
    properties,
    template,
    templates,
    controlStyle,
    cellStyle,
    fontStyle,
    style,
    expression,
    elementType,
    factor,
    multiplier,
    other,
    helperFields,
    usageType,
    sublist,
    validation,
    precision,
    scale,
    codes,
    initialAnswer,
    defaultAnswer,
    axis,
    element,
    categories,
    field,
    class,
}

export type LocalType = IntrinsicType | Type | ObjectType;

/**
 * 由于DS脚本不要求本地变量声明绑定类型，并允许隐式的类型转换，所以同一个符号可能存在不同的类型。
 * 此类型用于管理一个符号在脚本不同位置的不同类型。
 * 例如：
 * ```vb
 * Dim t
 * t = ""  '此时 t 的数据类型是字符串
 * t = 123 '此时 t 的数据类型是整型，可以直接赋值，程序会自动隐式转换
 *
 * ```
 */
export interface SymbolTracker {
    /**
     * 当前符号的最新的类型
     */
    latest(): LocalType;
    /**
     * 向节点表中追加节点，类型为最新的类型
     * @param node
     */
    appendNode(node: Node): void;
    /**
     * 获取指定节点的对应类型，如果节点不在列表中，返回`undefined`
     * @param node
     */
    getNodeType(node: Node): LocalType | undefined;
    /**
     * 获取在指定位置的此符号类型
     * @param position 文本位置
     */
    getTypeAt(position: number): LocalType | undefined;
    /**
     * 设置指定节点的类型
     * @param node 指定节点
     * @param type 对应的类型
     */
    setNodeType(node: Node, type: LocalType): void;
    /**
     * 遍历节点表
     * @param callback
     */
    forEachNode(callback: (node: Node) => void): void;
    /**
     * 当节点类型发生变化时触发的事件
     * @param handler 事件回调函数
     */
    onNodeTypeChanged(handler: SymbolTypeUpdatedEventHandler): void;
}

export type SymbolDecl = Declaration | Signature<SignatureKind> | PPDefine | MDMNamedObject;
export type SymbolTypeUpdatedEventHandler = (type: LocalType, node: Node) => void;

export interface SymbolBase {
    readonly kind: SymbolKind;
    readonly literalType?: LiteralType;
    readonly checkFlag: CheckFlag;
    readonly mdmKind: SymbolMDMKind;
    readonly sectionKind: SectionKind;
    readonly defaultMember?: Symbol;
    readonly parent?: Symbol;
    readonly categories?: Symbol;
    readonly initialization?: string | number | boolean;
}

export interface Symbol extends SymbolBase {
    readonly id: number;
    readonly name: string;
    readonly escapedName: string;
    readonly kind: SymbolKind;
    readonly literalType?: LiteralType;
    readonly checkFlag: CheckFlag;
    readonly mdmKind: SymbolMDMKind;
    readonly sectionKind: SectionKind;
    readonly locals: ReadonlyMap<string, Symbol>;  // 当符号为本地元数据变量定义时，表示Category元素定义
    readonly members: ReadonlyMap<string, Symbol>; // 当符号为本地元数据变量定义时，表示下级Fields定义
    readonly defaultMember?: Symbol;
    readonly parent?: Symbol;
    readonly declaration: SymbolDecl;
    readonly parameters: Symbol[];
    readonly tracker: SymbolTracker;
    readonly initialization?: string | number | boolean;
    readonly undefined?: PPUndef;
    readonly indexDimensions: number[];
    /**
     * 当前最新的类型
     */
    readonly type: LocalType;
    // methods
    is(flag: SymbolKind): boolean;
    isCheckFlag(flag: CheckFlag): boolean;
    isSection(flag: SectionKind): boolean;
    isMDM(flag: SymbolMDMKind): boolean;
    set<P extends keyof SymbolBase>(propName: P, value: SymbolBase[P]): void;
    /**
     * 获取最终的默认属性符号。
     */
    getDefaultProperty(): Symbol;
    /**
     * 获取符合判定函数条件的默认类型属性
     * @param predicate 判定回调函数
     * @returns
     */
    getMaybeDefaultProperty(predicate: Predicate<Symbol>): Symbol | undefined;
}

/**
 * 用来标记符号表类型。
 * 脚本符号存在允许的同名：
 * + 宏(macro)允许和任何符号同名
 * + Section命名允许和变量或函数同名
 */
export enum SymbolScope {
    local    = 1 << 0,
    function = 1 << 1,
    metadata = 1 << 2,
    macro    = 1 << 3,
    section  = 1 << 4,
    declare  = 1 << 5,
}

export interface SymbolPositionElement {
    readonly position: number;
    readonly macros: ReadonlyMap<string, Symbol>;
    readonly local: ReadonlyMap<string, Symbol>;
}

export interface ReadonlySymbolTable {
    _symbolTableBrand: any;
    readonly size: number;
    get(fullNames: string[]): Symbol | undefined;
    get(name: string, scope?: SymbolScope): Symbol | undefined;
    has(name: string, scope?: SymbolScope): boolean;
    forEach(callback: (s: Symbol, scope: SymbolScope) => any, scope?: SymbolScope, thisArg?: any): void;
    values(scope?: SymbolScope): Symbol[];
    table(scope: SymbolScope): ReadonlyMap<string, Symbol>;
}

export interface SymbolTable extends ReadonlySymbolTable {
    set(symbols: Symbol[], scope?: SymbolScope, onConflict?: Func<Symbol, void>): void;
    set(symbol: Symbol, scope?: SymbolScope, onConflict?: Func<Symbol, void>): void;
    delete(name: string, scope?: SymbolScope, position?: number): boolean;
    clear(): void;
    insert(insertTable: ReadonlySymbolTable, position: number, onConflict?: Func<Symbol, void>): void;
    insert(insertTable: ReadonlySymbolMap, position: number, scope?: SymbolScope, onConflict?: Func<Symbol, void>): void;
    positionAt(position: number): readonly Symbol[];
    reference(table: ReadonlySymbolMap, scope: SymbolScope): void;
}

export type SymbolMap = Map<string, Symbol>;
export type ReadonlySymbolMap = ReadonlyMap<string, Symbol>;
export type SymbolTableLike = SymbolMap | SymbolTable;
export type ReadonlySymbolTableLike = ReadonlySymbolMap | ReadonlySymbolTable;

//
//  Type
//

export enum TypeFlag {
    undefined    = 0,
    void         = 1 << 0,
    any          = 1 << 1,
    string       = 1 << 2,
    date         = 1 << 3,
    long         = 1 << 4,
    double       = 1 << 5,
    boolean      = 1 << 6,
    null         = 1 << 7,
    categorical  = 1 << 8,
    object       = 1 << 9,
    arrayType    = 1 << 10,  // 用来表示`any[]`这样格式的BindingType
    stringLike   = string | date | TypeFlag.null,
    numberLike   = long | double | boolean | TypeFlag.null,
    intrinsic    = TypeFlag.undefined | any | string | date | long | double | boolean | TypeFlag.null | categorical,
    basic        = any | string | date | long | double | boolean | categorical | arrayType,
}

export interface TypeCompareResult {
    readonly success: boolean;
    readonly type: LocalType;
}

export interface IntrinsicType {
    readonly flag: TypeFlag;
    readonly name: string;
    readonly escapedName: string;
    readonly rank: number;
    readonly original?: LocalType;
    /**
     * 复制当前的类型对象，添加指定的数组维度，并返回新的对象。
     * @param rank 维度数
     * @param dimensions 多维度索引值
     */
    rankType(rank: number): IntrinsicType;
    is(flag: TypeFlag): boolean;
    isType(): this is Type;
    isObjectType(): this is ObjectType;
    isCompatible(type: IntrinsicType, ignoreArray?: boolean): boolean;
    /**
     *
     * 检查两个类型是否允许互相赋值或者计算，判断能否将`type`赋值给当前对象，
     * 对于二元操作符，派生类和基类之间不允许相互赋值和计算，
     * 只有在作为参数传入函数时，才会允许 '派生类->基类' 的赋值，反方向不允许。
     *
     * @param type 被检查类型
     * @param allowInherit 是否允许派生类
     * @param ignoreArray 是否忽略数组定义，仅对比类型是否相同
     * @returns 检查后符合计算结果的类型，如果允许派生类，当类型正确时将返回派生类
     */
    compare(type: LocalType, allowInherit?: boolean, ignoreArray?: boolean): TypeCompareResult;
}

export interface Type extends IntrinsicType {
    readonly id: number;
    readonly symbol: Symbol;
    readonly inherits: Type[];
    readonly implements: Type[];
    rankType(rank: number): Type;
}

export enum ObjectTypeFlag {
    null          = 0,
    class         = 1 << 0,
    interface     = 1 << 1,
    array         = 1 << 2,
    module        = 1 << 3,
    enum          = 1 << 4,
    referenceType = ObjectTypeFlag.class | ObjectTypeFlag.interface,
}

export interface ObjectType extends Type {
    objectTypeFlag: ObjectTypeFlag;
    members: ReadonlyMap<string, Symbol>;
}

export enum IndexKind {
    string,
    number
}

export enum OptionFlag {
    implicit         = 1 << 0,
    explicit         = 1 << 1,
    globalVariables  = 1 << 2,
}

export interface TypeChecker {
    clear(): void;
    onError(handler?: Func<Diagnostic, void>): void;
    createSymbol(flag: SymbolKind, name: string, checkFlag: CheckFlag, declaration: Declaration | PPDefine | Signature<SignatureKind>, type?: LocalType): Symbol;
    updateOptions(options: ScriptOptionUpdateParam): void;
    setGlobalDeclarations(table?: ReadonlyMap<string, Symbol>): void;
    setLocalDeclarations(table?: ReadonlyMap<string, Symbol>): void;
    setLocalMetadatas(table?: ReadonlyMap<string, Symbol>): void;
    createType(flag: TypeFlag, name: string, symbol: Symbol): Type;
    createObjectType(flag: ObjectTypeFlag, name: string, symbol: Symbol): ObjectType;
    checkDeclareFile(sourceFile: SourceFile): ReadonlyMap<string, Symbol>;
    checkDeclareFiles(files: readonly SourceFile[]): ReadonlyMap<string, Symbol>;
    checkSourceFile(source: SourceFile): ReadonlySymbolTable;
}

export interface Converter {
    get factory(): NodeFactory;
    convertNodeToToken<T extends SyntaxKind>(node: Node, kind: T): Token<T>;
    convertNodeToNumberLiteral(node: Node): NumberLiteral;
    convertNodeToStringLiteral(expression: Node): StringLiteral;
    convertNodesToStringLiteral(nodes: readonly Node[], separator: string): StringLiteral;
    convertNodeToIdentifier(node: Node): Identifier;
    convertStringLiteralToIdentifiers(literal: StringLiteral, delimiter: string): Identifier[];
    convertStringLiteralToBooleanLiteral(literal: StringLiteral): TrueLiteral | FalseLiteral;
    convertIdentifierToStringLiteral(identifier: Identifier): StringLiteral;
    convertNumberLiteralToStringLiteral(literal: NumberLiteral): StringLiteral;
    convertStringLiteralToNumberLiteral(literal: StringLiteral, value: number): NumberLiteral;
    convertNodeToDeclaration(node: Node): Declaration;
    convertIdentifierToVariableDeclaration(identifier: Identifier): VariableDecl;
    convertIdentifierToArrayDeclaration(identifier: Identifier): ArrayDecl;
}

export interface ObjectAllocator {
    getIntrinsicTypeConstructor(): new (flag: TypeFlag, name: string, rank?: number) => IntrinsicType;
    getTypeConstructor(): new (id: number, flag: TypeFlag, name: string, symbol: Symbol) => Type;
    getSymbolConstructor(): new (id: number, name: string, flag: SymbolKind, declaration?: SymbolDecl, type?: LocalType) => Symbol;
    getNodeConstructor(): new (flag: SyntaxKind, start: number, end: number) => Node;
    getNodeCopyConstructor(): new (node: Node) => Node;
    getIdentifierConstructor(): new (flag?: SyntaxKind.identifier, start?: number, end?: number) => Identifier;
    getMetadataArealLikeConstructor(): new <T extends MDMArealBase>(flag: MDMTokenKind, start: number, end: number) => MDMArealLike<T>;
    getMetadataPropertiesLikeConstructor(): new <T extends MDMKeyValuePair, U extends MDMDeclaration>(flag: U["kind"], start: number, end: number) => MDMPropertiesLike<T, U>;
    getMetadataStyleConstructor(): new (flag: SyntaxKind.mdmStyle, start: number, end: number, name: Identifier) => MDMStyle;
    getMetadataCollectionConstructor(): new <T extends MDMLabeledObject>(flag: MDMTokenKind, start: number, end: number) => MDMCollection<T>;
    getSourceFileConstructor(): new (flag: SyntaxKind.sourceFile, start: number, end: number) => SourceFile;
    getSymbolTableConstructor(): new () => SymbolTable;
}

export enum SyntaxKind {

    eof,               // End of file
    unknown,

    openParenthesis,   // (
    closeParenthesis,  // )

    openBracket,    // [
    closeBracket,   // ]

    openCurly,  // {
    closeCurly, // }

    // punctuation
    caret,                 // ^
    ampersand,             // &
    dot,                   // .
    dotDot,                // ..
    dotDotDot,             // ...
    comma,                 // ,
    backSlash,             // \
    semicolon,             // ;
    colon,                 // :
    quotation,             // "
    apostrophe,            // '

    // C/C++ preprocessor punctuation
    barbar,              // ||
    ampersandAmpersand,  // &&
    exclamationEqual,    // !=
    exclamation,         // !
    equalEqual,          // ==

    // Operators
    plus,             // +
    minus,            // -
    equal,            // =
    mod,              // mod
    slash,            // /
    asterisk,         // *
    equalAsterisk,    // =*

    // relational
    greater,       // >
    less,          // <
    greaterEqual,  // >=
    lessEqual,     // <=
    notEqual,      // <>

    // The Document-Level (\\.) Operator
    backslashBackslashDot,

    // Keywords

    paper,

    routingKeyword,

    // On Error
    onKeyword,
    errorKeyword,
    resumeKeyword,

    // Option Keywords
    optionKeyword,
    implicitKeyword,  // Part of an `Option Implicit` statement, which specifies that variables do not need to be explicitly defined.
    explicitKeyword,  // Part of an `Option Explicit` statement, which specifies that all variables must be explicitly defined.
    globalVariablesKeyword, // Part of an `Option GlobalVariables` statement, which specifies that variables defined in the main script are visible to Functions and Sub procedures.

    // Declaration
    dimKeyword,
    constKeyword,
    functionKeyword,
    subKeyword,

    trueKeyword,
    falseKeyword,
    nullKeyword,

    setKeyword,

    endKeyword,
    exitKeyword,
    gotoKeyword,

    // Assert
    isKeyword,    // Introduces an Is [Not] Null statement, which is used to test if an object reference is assigned to a variable.
    likeKeyword,  // Returns the boolean value True if a text string matches a pattern.

    // logical keyword
    andKeyword,     // and
    orKeyword,      // or
    xorKeyword,     // xor
    notKeyword,     // not

    // condition
    ifKeyword,
    thenKeyword,
    elseifKeyword,
    elseKeyword,

    // for loop
    forKeyword,
    toKeyword,
    stepKeyword,
    inKeyword,
    eachKeyword,
    nextKeyword,

    // while loop
    whileKeyword,

    // do loop
    doKeyword,
    loopKeyword,
    untilKeyword,

    // select statment
    selectKeyword,
    caseKeyword,

    // with statement
    withKeyword,

    // section statement
    sectionKeyword,
    jobKeyword,
    inputDataSourceKeyword,
    outputDataSourceKeyword,
    globalSQLVariablesKeyword,
    metadataKeyword,
    loggingKeyword,
    eventKeyword,

    // declare dectator
    asKeyword,
    enumKeyword,
    defaultKeyword,
    moduleKeyword,
    interfaceKeyword,
    classKeyword,
    implementsKeyword,
    inheritsKeyword,
    propertyKeyword,
    paramArrayKeyword,
    readonlyKeyword,
    writeonlyKeyword,
    optionalKeyword,

    // preprocessor
    ppIncludeKeyword,
    ppDefineKeyword,
    ppLineKeyword,
    ppIfKeyword,
    ppIfDefKeyword,
    ppIfNDefKeyword,
    ppElIfKeyword,
    ppElseKeyword,
    ppEndIfKeyword,
    ppErrorKeyword,
    ppUndefKeyword,

    // node flag
    sourceFile,
    modifier,

    identifier,

    stringLiteral,
    numberLiteral,
    categoricalItemLiteral,
    categoricalLiteral,
    trueLiteral,
    falseLiteral,
    nullLiteral,

    location,

    localDecls,
    varDecl,
    constDecl,
    arrayDecl,
    funcDecl,

    constSignature,
    arrayTypeSignature,
    parameterSignature,
    propertySignature,
    functionSignature,
    enumMemberSignature,
    enumSignature,
    interfaceSignature,
    classSignature,
    moduleSignature,

    inheritLike,

    emptyExpr,
    unaryExpr,
    binaryExpr,
    iteration,
    callExpr,
    memberExpr,
    indexExpr,
    selectCaseComparison,
    parameterMacroCallExpr,
    objectCollectionIterator,

    block,
    exprStat,
    ifStat,
    forStat,
    forEachStat,
    doStat,
    whileStat,
    selectCaseClause,
    selectStat,
    withStat,
    sectionStat,
    optionStat,
    jumpStat,   // goto / on error / exit
    resumeStat, // resume next
    onErrorStat,

    jobSection,
    inputDataSourceSection,
    outputDataSourceSection,
    globalSQLVariablesSection,
    metadataSection,
    loggingSection,
    routingSection,
    eventSection,

    // Pre-processor statement
    ppInclude,
    ppDefine,
    ppDefineParameter,
    ppDefineStringizer,
    ppDefineTokenPaster,
    ppUndef,
    ppIf,
    ppIfDef,
    ppIfNDef,
    ppError,
    ppLine,

    bindingType,

    // metadata keywords
    mdmStyleKeyword,                    // style
    mdmLabelStyleKeyword,               // labelstyle
    mdmTemplatesKeyword,                // templates
    mdmLongKeyword,                     // long
    mdmDoubleKeyword,                   // double
    mdmTextKeyword,                     // text
    mdmDateKeyword,                     // date
    mdmBooleanKeyword,                  // boolean
    mdmCategoricalKeyword,              // categorical
    mdmDefineKeyword,                   // define
    mdmInfoKeyword,                     // info
    mdmCompoundKeyword,                 // compound
    mdmBlockKeyword,                    // block
    mdmPageKeyword,                     // page
    mdmDBKeyword,                       // db
    mdmCodesKeyword,                    // codes
    mdmExpressionKeyword,               // expression
    mdmDeriveElementsKeyword,           // deriveelements
    mdmNoDeriveElementsKeyword,         // noderiveelements
    mdmValidationKeyword,               // validation
    mdmInitialAnswerKeyword,            // initialanswer
    mdmDefaultAnswerKeyword,            // defaultanswer
    mdmNocaseDataKeyword,               // nocasedata
    mdmUnversionedKeyword,              // unversioned
    mdmAxisKeyword,                     // axis
    mdmUsageTypeKeyword,                // usagetype
    mdmHelperFieldsKeyword,             // helperfields
    mdmPrecisionKeyword,                // precision
    mdmRotateKeyword,                   // rotate
    mdmRotKeyword,                      // rot
    mdmRandomizeKeyword,                // randomize
    mdmRanKeyword,                      // ran
    mdmReverseKeyword,                  // reverse
    mdmRevKeyword,                      // rev
    mdmAscendingKeyword,                // ascending
    mdmAscKeyword,                      // asc
    mdmDescendingKeyword,               // descending
    mdmDescKeyword,                     // desc
    mdmNamespaceKeyword,                // namespace
    mdmUseKeyword,                      // use
    mdmSublistKeyword,                  // sublist
    mdmExclusiveKeyword,                // exclusive
    mdmFactorKeyword,                   // factor
    mdmKeycodeKeyword,                  // keycode
    mdmFixKeyword,                      // fix
    mdmCanFilterKeyword,                // canfilter
    mdmNoFilterKeyword,                 // nofilter
    mdmOtherKeyword,                    // other
    mdmMultiplierKeyword,               // multiplier
    mdmElementTypeKeyword,              // elementtype
    mdmDKKeyword,                       // dk
    mdmREFKeyword,                      // ref
    mdmNAKeyword,                       // na
    mdmFieldsKeyword,                   // fields
    mdmRowKeyword,                      // row
    mdmColumnKeyword,                   // column
    mdmExpandKeyword,                   // expand
    mdmNoExpandKeyword,                 // noexpand
    mdmGridKeyword,                     // grid
    mdmInlineKeyword,                   // inline
    mdmLCLKeyword,                      // lcl
    mdmScaleKeyword,                    // scale

    mdmLabel,
    mdmHierarchicalView,
    mdmProperty,
    mdmProperties,
    mdmTemplate,
    mdmTemplates,
    mdmStyle,
    mdmCellStyle,
    mdmControlStyle,
    mdmFontStyle,
    mdmCategory,
    mdmCategoryExpression,
    mdmElementType,
    mdmKeycode,
    mdmFactor,
    mdmMultiplier,
    mdmOther,
    mdmSublist,
    mdmPrecision,
    mdmScale,
    mdmCodes,
    mdmAxis,
    mdmValidation,
    mdmFieldExpression,
    mdmInitialAnswer,
    mdmDefaultAnswer,
    mdmUsageType,
    mdmCategories,
    mdmClass,
    mdmField,
    mdmValueRange,
    mdmHelperFields,
    mdmAreaName,    // AreaName lcl ( [language] [, [user-context] [, [label-type] ]] )
    mdmDocument,

    lastToken = nullLiteral,
    firstNode = sourceFile,
    lastNode = ppLine,
    firstKeyword = paper,
    lastKeyword = sectionKeyword,
    firstSectionKeyword = jobKeyword,
    lastSectionKeyword = eventKeyword,
    firstDeclareKeyword = asKeyword,
    lastDeclareKeyword = optionalKeyword,
    firstStatement = block,
    lastStatement = onErrorStat,
    firstSection = jobSection,
    lastSection = eventSection,
    firstPPKeyword = ppIncludeKeyword,
    lastPPKeyword = ppUndefKeyword,
    firstPPStatement = ppInclude,
    lastPPStatement = ppLine,
    firstPPOperator = barbar,
    lastPPOperator = equalEqual,
    firstMDMKeyword = mdmStyleKeyword,
    lastMDMKeyword = mdmScaleKeyword,
}

export type KeywordTokenKind =
| SyntaxKind.andKeyword
| SyntaxKind.caseKeyword
| SyntaxKind.constKeyword
| SyntaxKind.dimKeyword
| SyntaxKind.doKeyword
| SyntaxKind.eachKeyword
| SyntaxKind.elseKeyword
| SyntaxKind.elseifKeyword
| SyntaxKind.endKeyword
| SyntaxKind.errorKeyword
| SyntaxKind.exitKeyword
| SyntaxKind.explicitKeyword
| SyntaxKind.eventKeyword
| SyntaxKind.falseKeyword
| SyntaxKind.forKeyword
| SyntaxKind.functionKeyword
| SyntaxKind.globalVariablesKeyword
| SyntaxKind.gotoKeyword
| SyntaxKind.ifKeyword
| SyntaxKind.implicitKeyword
| SyntaxKind.inKeyword
| SyntaxKind.isKeyword
| SyntaxKind.likeKeyword
| SyntaxKind.loopKeyword
| SyntaxKind.mod
| SyntaxKind.nextKeyword
| SyntaxKind.notKeyword
| SyntaxKind.nullKeyword
| SyntaxKind.onKeyword
| SyntaxKind.optionKeyword
| SyntaxKind.orKeyword
| SyntaxKind.paper
| SyntaxKind.resumeKeyword
| SyntaxKind.sectionKeyword
| SyntaxKind.selectKeyword
| SyntaxKind.setKeyword
| SyntaxKind.stepKeyword
| SyntaxKind.subKeyword
| SyntaxKind.thenKeyword
| SyntaxKind.toKeyword
| SyntaxKind.trueKeyword
| SyntaxKind.untilKeyword
| SyntaxKind.whileKeyword
| SyntaxKind.withKeyword
| SyntaxKind.xorKeyword
;

export type DelcareKeywordKind =
| SyntaxKind.asKeyword
| SyntaxKind.moduleKeyword
| SyntaxKind.interfaceKeyword
| SyntaxKind.classKeyword
| SyntaxKind.enumKeyword
| SyntaxKind.implementsKeyword
| SyntaxKind.inheritsKeyword
| SyntaxKind.paramArrayKeyword
| SyntaxKind.propertyKeyword
| SyntaxKind.readonlyKeyword
| SyntaxKind.writeonlyKeyword
| SyntaxKind.optionalKeyword
| SyntaxKind.defaultKeyword
;

export type DmsSectionKeywordKind =
| SyntaxKind.jobKeyword
| SyntaxKind.inputDataSourceKeyword
| SyntaxKind.outputDataSourceKeyword
| SyntaxKind.globalSQLVariablesKeyword
| SyntaxKind.metadataKeyword
| SyntaxKind.loggingKeyword
| SyntaxKind.eventKeyword
| SyntaxKind.routingKeyword // Routing 只能在 MDMDocument 生成的Routing文件中才识别为关键字
;

export type MDMKeywordKind =
| SyntaxKind.mdmStyleKeyword
| SyntaxKind.mdmLabelStyleKeyword
| SyntaxKind.mdmTemplatesKeyword
| SyntaxKind.mdmLongKeyword
| SyntaxKind.mdmDoubleKeyword
| SyntaxKind.mdmTextKeyword
| SyntaxKind.mdmDateKeyword
| SyntaxKind.mdmBooleanKeyword
| SyntaxKind.mdmCategoricalKeyword
| SyntaxKind.mdmDefineKeyword
| SyntaxKind.mdmInfoKeyword
| SyntaxKind.mdmCompoundKeyword
| SyntaxKind.mdmBlockKeyword
| SyntaxKind.mdmPageKeyword
| SyntaxKind.mdmDBKeyword
| SyntaxKind.mdmCodesKeyword
| SyntaxKind.mdmExpressionKeyword
| SyntaxKind.mdmDeriveElementsKeyword
| SyntaxKind.mdmNoDeriveElementsKeyword
| SyntaxKind.mdmValidationKeyword
| SyntaxKind.mdmInitialAnswerKeyword
| SyntaxKind.mdmDefaultAnswerKeyword
| SyntaxKind.mdmNocaseDataKeyword
| SyntaxKind.mdmUnversionedKeyword
| SyntaxKind.mdmAxisKeyword
| SyntaxKind.mdmUsageTypeKeyword
| SyntaxKind.mdmHelperFieldsKeyword
| SyntaxKind.mdmPrecisionKeyword
| SyntaxKind.mdmRotateKeyword
| SyntaxKind.mdmRotKeyword
| SyntaxKind.mdmRandomizeKeyword
| SyntaxKind.mdmRanKeyword
| SyntaxKind.mdmReverseKeyword
| SyntaxKind.mdmRevKeyword
| SyntaxKind.mdmAscendingKeyword
| SyntaxKind.mdmAscKeyword
| SyntaxKind.mdmDescendingKeyword
| SyntaxKind.mdmDescKeyword
| SyntaxKind.mdmNamespaceKeyword
| SyntaxKind.mdmUseKeyword
| SyntaxKind.mdmSublistKeyword
| SyntaxKind.mdmExclusiveKeyword
| SyntaxKind.mdmFactorKeyword
| SyntaxKind.mdmKeycodeKeyword
| SyntaxKind.mdmFixKeyword
| SyntaxKind.mdmCanFilterKeyword
| SyntaxKind.mdmNoFilterKeyword
| SyntaxKind.mdmOtherKeyword
| SyntaxKind.mdmMultiplierKeyword
| SyntaxKind.mdmElementTypeKeyword
| SyntaxKind.mdmDKKeyword
| SyntaxKind.mdmREFKeyword
| SyntaxKind.mdmNAKeyword
| SyntaxKind.mdmFieldsKeyword
| SyntaxKind.mdmRowKeyword
| SyntaxKind.mdmColumnKeyword
| SyntaxKind.mdmExpandKeyword
| SyntaxKind.mdmNoExpandKeyword
| SyntaxKind.mdmGridKeyword
| SyntaxKind.mdmInlineKeyword
| SyntaxKind.mdmLCLKeyword
| SyntaxKind.mdmScaleKeyword
;

export type MDMFieldSuffixKind =
| SyntaxKind.mdmPrecision
| SyntaxKind.mdmScale
| SyntaxKind.mdmCodes
| SyntaxKind.mdmAxis
| SyntaxKind.mdmValidation
| SyntaxKind.mdmFieldExpression
| SyntaxKind.mdmInitialAnswer
| SyntaxKind.mdmDefaultAnswer
;

export type MDMDeclarationKind =
| MDMFieldSuffixKind
| SyntaxKind.mdmProperty
| SyntaxKind.mdmProperties
| SyntaxKind.mdmTemplate
| SyntaxKind.mdmTemplates
| SyntaxKind.mdmStyle
| SyntaxKind.mdmFontStyle
| SyntaxKind.mdmControlStyle
| SyntaxKind.mdmCellStyle
| SyntaxKind.mdmClass
| SyntaxKind.mdmField
| SyntaxKind.mdmHierarchicalView
| SyntaxKind.mdmAreaName
| SyntaxKind.mdmCategory
| SyntaxKind.mdmCategories
| SyntaxKind.mdmSublist
| SyntaxKind.mdmOther
| SyntaxKind.mdmMultiplier
| SyntaxKind.mdmCategoryExpression
| SyntaxKind.mdmElementType
| SyntaxKind.mdmKeycode
| SyntaxKind.mdmFactor
| SyntaxKind.mdmHelperFields
| SyntaxKind.mdmUsageType
;

export type DeclarationKind =
| SyntaxKind.varDecl
| SyntaxKind.constDecl
| SyntaxKind.arrayDecl
| SyntaxKind.funcDecl
| SyntaxKind.sectionStat
| SyntaxKind.jobSection
| SyntaxKind.inputDataSourceSection
| SyntaxKind.outputDataSourceSection
| SyntaxKind.globalSQLVariablesSection
| SyntaxKind.metadataSection
| SyntaxKind.loggingSection
| SyntaxKind.eventSection
| SyntaxKind.routingSection
| SyntaxKind.location
| SyntaxKind.sourceFile
| MDMDeclarationKind
;

export type MDMFieldTokenKind =
| SyntaxKind.loopKeyword
| SyntaxKind.mdmLongKeyword
| SyntaxKind.mdmDoubleKeyword
| SyntaxKind.mdmTextKeyword
| SyntaxKind.mdmDateKeyword
| SyntaxKind.mdmBooleanKeyword
| SyntaxKind.mdmCategoricalKeyword
| SyntaxKind.mdmInfoKeyword
| SyntaxKind.mdmDefineKeyword
| SyntaxKind.mdmGridKeyword
| SyntaxKind.mdmCompoundKeyword
| SyntaxKind.mdmBlockKeyword
| SyntaxKind.mdmPageKeyword
;

export type DeclareModifierKind =
| SyntaxKind.readonlyKeyword
| SyntaxKind.writeonlyKeyword
| SyntaxKind.optionalKeyword
| SyntaxKind.defaultKeyword
;

export type PunctuationKind =
| SyntaxKind.openBracket
| SyntaxKind.closeBracket
| SyntaxKind.openCurly
| SyntaxKind.closeCurly
| SyntaxKind.openParenthesis
| SyntaxKind.closeParenthesis
| SyntaxKind.dot
| SyntaxKind.dotDot
| SyntaxKind.dotDotDot
| SyntaxKind.caret
| SyntaxKind.comma
| SyntaxKind.colon
;

export type UnaryOperatorKind =
| SyntaxKind.notKeyword
| SyntaxKind.exclamation
| SyntaxKind.minus
;

export type BinaryOperatorKind =
| SyntaxKind.andKeyword
| SyntaxKind.orKeyword
| SyntaxKind.xorKeyword
| SyntaxKind.plus
| SyntaxKind.minus
| SyntaxKind.asterisk
| SyntaxKind.slash
| SyntaxKind.mod
| SyntaxKind.greater
| SyntaxKind.greaterEqual
| SyntaxKind.less
| SyntaxKind.lessEqual
| SyntaxKind.equal
| SyntaxKind.equalAsterisk
| SyntaxKind.equalEqual
| SyntaxKind.barbar
| SyntaxKind.ampersandAmpersand
| SyntaxKind.notEqual
| SyntaxKind.exclamationEqual
| SyntaxKind.likeKeyword
| SyntaxKind.isKeyword
;

export type LiteralKind =
| SyntaxKind.numberLiteral
| SyntaxKind.stringLiteral
| SyntaxKind.categoricalItemLiteral
| SyntaxKind.categoricalLiteral
| SyntaxKind.trueLiteral
| SyntaxKind.falseLiteral
| SyntaxKind.nullLiteral
;

export type PPPunctuationKind =
| SyntaxKind.ampersandAmpersand
| SyntaxKind.barbar
| SyntaxKind.exclamationEqual
| SyntaxKind.exclamation
;

export type PPKeywordKind =
| SyntaxKind.ppDefineKeyword
| SyntaxKind.ppElIfKeyword
| SyntaxKind.ppElseKeyword
| SyntaxKind.ppEndIfKeyword
| SyntaxKind.ppErrorKeyword
| SyntaxKind.ppIfKeyword
| SyntaxKind.ppIfDefKeyword
| SyntaxKind.ppIncludeKeyword
| SyntaxKind.ppLineKeyword
| SyntaxKind.ppUndefKeyword
;

export interface Comment extends ReadonlyTextRange {
    readonly comments: string[];
}

export enum TokenFlags {
    none = 0,
    precedingLinebreak   = 1 << 0,
    unterminated         = 1 << 1,
    multiLineString      = 1 << 2,
    singleLineString     = 1 << 3,
    octal                = 1 << 4,
    hex                  = 1 << 5,
    decimal              = 1 << 6,
    float                = 1 << 7,
    mrsKeyword           = 1 << 8,
    declareKeyword       = 1 << 9,
    dmsKeyword           = 1 << 10,
    metadataKeyword      = 1 << 11,
    operator             = 1 << 12,
    ppOperator           = 1 << 13,
    integer              = octal | hex | decimal,
    numberLike           = octal | hex | decimal | float,
    keywordLike          = mrsKeyword | declareKeyword,
}

// Node

export interface Node extends ReadonlyTextRange {
    readonly id: number;
    readonly aheadComments: Comment[];     // Comments ahead node
    readonly kind: SyntaxKind;              // Node type flag
    readonly parent?: Node;                 // Parent node
    readonly symbol?: Symbol;               // Symbol delcare by node
    readonly locals?: SymbolTable;          // Locals associated with node
    readonly text: string;                  // Node Text
    readonly optionFlag: OptionFlag;
    readonly optionNode?: OptionStatement;
    readonly children: readonly Node[];
    /* @internal */ readonly expands: MacroExpandToken[];
    // methods
    asParent(parent?: Node): void;
    getParent(predicate?: Predicate<Node>): Node | undefined;
    getParent<T extends Node>(predicate: Predicate<T>): T | undefined;
    setSymbol(symbol: Symbol): void;
    getSourceFile(): SourceFile;
    getWidth(): number;
    childrenCount(): number;
    childAt(index: number): Node;
    asChildren(...children: readonly (Node | undefined)[]): void;
    removeChild(predicate: Predicate<Node>): void;
    removceAllChildren(): void;
    forEachChild<T>(nodeCallback: (node: Node) => T, nodeArrayCallback?: (nodes: NodeArray<Node>) => T): T | undefined;
    // test
    is<T extends Node>(kind: T["kind"]): this is T;
    isToken<T extends SyntaxKind>(kind: T): this is Token<T>;
    isModifierToken(): this is ModifierToken<DeclareModifierKind>;
    isModifierToken<T extends DeclareModifierKind>(kind: T): this is ModifierToken<T>
    isUnaryOperatorToken(): this is Token<UnaryOperatorKind>;
    isUnaryOperatorToken<T extends UnaryOperatorKind>(kind: T): this is Token<T>;
    isBinaryOperatorToken(): this is Token<BinaryOperatorKind>;
    isBinaryOperatorToken<T extends BinaryOperatorKind>(kind: T): this is Token<T>;
    isPunctuation(): this is Punctuation<PunctuationKind>;
    isPunctuation<T extends PunctuationKind>(kind: T): this is Punctuation<T>;
    isSourceFile(): this is SourceFile;
    isIdentifier(): this is Identifier;
    isStringLiteral(): this is StringLiteral;
    isNumberLiteral(): this is NumberLiteral;
    isCategoricalLiteral(): this is CategoricalLiteral;
    isTrueLiteral(): this is TrueLiteral;
    isFalseLiteral(): this is FalseLiteral;
    isBooleanLiteral(): this is TrueLiteral | FalseLiteral;
    isNullLiteral(): this is NullLiteral;
    isLiteral(): this is Literal;
    isExpression(): this is Expression;
    isExpression<T extends Expression>(kind: T["kind"]): this is T;
    isAssignmentExpression(): this is AssignmentExpression;
    isMemberExpressionObject(): this is MemberExpressionObject;
    isCallee(): this is Callee;
    isSignature(): this is Signature<SignatureKind>;
    isSignature<T extends Signature<SignatureKind>>(kind: T["kind"]): this is T;
    isDeclaration(): this is Declaration;
    isDeclaration<T extends Declaration>(kind: T["kind"]): this is T;
    isStatement(): this is Statement;
    isStatement<T extends Statement>(kind: T["kind"]): this is T;
    isPreprocessor(): this is Preprocessor;
    isPreprocessor<T extends Preprocessor>(kind: T["kind"]): this is T;
    isMissingNode(): boolean;
    isMDMCollection(): this is MDMCollection<MDMLabeledObject>;
    isMDMField(): this is MDMField;
    isMDMDbQuestions(): this is MDMDbQuestions;
    isMDMPage(): this is MDMPage;
}

export interface NodeArray<T extends ReadonlyTextRange> extends ReadonlyArray<T>, ReadonlyTextRange {
}

export interface MissingNodeArray<T extends ReadonlyTextRange> extends NodeArray<T> {
    isMissing: true;
}

export interface MutableNodeArray<T extends TextRange> extends Array<T>, TextRange {
}

export interface Token<T extends SyntaxKind> extends Node {
    readonly kind: T;
    readonly text: string;
    readonly keyword?: KeywordTokenKind;
    readonly delcareKeyword?: DelcareKeywordKind;
    readonly declareModifier?: DeclareModifierKind;
    readonly punctuation?: PunctuationKind;
    readonly preprocessorPunctuation?: PPPunctuationKind;
}

export type Punctuation<T extends PunctuationKind> = Token<T>;
export type KeywordToken<T extends KeywordTokenKind> = Token<T>;
export type DeclareKeywordToken<T extends DelcareKeywordKind> = Token<T>;
export type ModifierToken<T extends DeclareModifierKind> = Token<T>;
export type PPPunctuation<T extends PPPunctuationKind> = Token<T>;

export enum LiteralType {
    number,
    octNumber,
    hexNumber,
    singleLineString,
    multiLineString,
    categorical,
    categoricalItem,
    true,
    false,
    null
}

export interface Literal extends Expression {
    readonly kind: LiteralKind;
    readonly text: string;
    readonly literalType: LiteralType;
}

export interface StringLiteral extends Literal {
    readonly kind: SyntaxKind.stringLiteral;
    readonly literalType: LiteralType.singleLineString | LiteralType.multiLineString;
}

export interface NumberLiteral extends Literal {
    readonly kind: SyntaxKind.numberLiteral;
    readonly literalType: LiteralType.number | LiteralType.octNumber | LiteralType.hexNumber;
    readonly value: number;
    readonly isFloat: boolean;
}

export interface CategoricalItemLiteral extends Literal {
    readonly kind: SyntaxKind.categoricalItemLiteral;
    readonly literalType: LiteralType.categoricalItem;
    readonly dotDot?: Token<SyntaxKind.dotDot>;
    readonly lower?: NumberLiteral | Identifier;       //  lower..upper
    readonly upper?: NumberLiteral | Identifier;       //  lower..upper
    readonly exclude?: Punctuation<SyntaxKind.caret>;  // 是否有^
    readonly isRange: boolean;
}

export interface CategoricalLiteral extends Literal {
    readonly kind: SyntaxKind.categoricalLiteral;
    readonly categories: NodeArray<CategoricalItemLiteral>;
}

export interface TrueLiteral extends Literal {
    readonly kind: SyntaxKind.trueLiteral;
}

export interface FalseLiteral extends Literal {
    readonly kind: SyntaxKind.falseLiteral;
}

export interface NullLiteral extends Literal {
    readonly kind: SyntaxKind.nullLiteral;
}

export interface Expression extends Node {
    _expressionBrand: any;
}

export interface EmptyExpr extends Expression {
    readonly kind: SyntaxKind.emptyExpr;
}

export interface Identifier extends Expression {
    readonly kind: SyntaxKind.identifier;
    readonly text: string;
}

export interface LineLocation extends Declaration {
    readonly kind: SyntaxKind.location;
    _locationBrand: any;
}

export interface Binding extends Node {
    readonly kind: SyntaxKind.bindingType;
    readonly type: NodeArray<Identifier> | ArrayTypeSignature;
    readonly as: DeclareKeywordToken<SyntaxKind.asKeyword>;
    readonly parent: Declaration | Signature<SignatureKind>;
}

export interface Declaration extends Statement {
    readonly kind: DeclarationKind;
    readonly name: Identifier;
    readonly bindingType?: Binding;
    readonly reference?: Node;
    _declarationBrand: any;
}

export interface VariableDecl extends Declaration {
    readonly kind: SyntaxKind.varDecl;
    _variableDeclarationBrand: any;
}

// array[2], array[2][], array[2][2][] ...
export interface ArrayDecl extends Declaration {
    readonly kind: SyntaxKind.arrayDecl;
    readonly dimensions: NodeArray<NumberLiteral | EmptyExpr>; // 数组的各个维度的容量，最后一维无限制
    readonly rank: number;                                           // 数组的维度数
}

export interface ConstantDeclaration extends Declaration {
    readonly kind: SyntaxKind.constDecl;
    readonly initialize: AssignmentExpression;
}

export interface IterationExpr extends Expression {
    readonly kind: SyntaxKind.iteration;
    readonly lower: Expression | NumberLiteral;
    readonly upper: Expression | NumberLiteral;
}

export interface FunctionDeclaration extends Declaration {
    readonly kind: SyntaxKind.funcDecl;
    readonly parameters: NodeArray<ParameterSignature>;
    readonly hasReturn: boolean;
    readonly body: Block;
    readonly locals: SymbolTable;
}

export type SignatureKind =
| SyntaxKind.constSignature
| SyntaxKind.arrayTypeSignature
| SyntaxKind.parameterSignature
| SyntaxKind.propertySignature
| SyntaxKind.functionSignature
| SyntaxKind.interfaceSignature
| SyntaxKind.classSignature
| SyntaxKind.moduleSignature
| SyntaxKind.enumMemberSignature
| SyntaxKind.enumSignature
;

export interface Signature<T extends SignatureKind> extends Statement {
    readonly kind: T;
    readonly name: Identifier;
    readonly module?: ModuleSignature;
}

export interface ArrayTypeSignature extends Signature<SyntaxKind.arrayTypeSignature> {
    readonly fullName: NodeArray<Identifier>;
    readonly rank: number;
}

export interface ConstSignature extends Signature<SyntaxKind.constSignature> {
    readonly bindingType: Binding;
}

export interface ParameterSignature extends Signature<SyntaxKind.parameterSignature> {
    readonly kind: SyntaxKind.parameterSignature;
    readonly declaration?: VariableDecl | ArrayDecl;
    readonly paramArray?: DeclareKeywordToken<SyntaxKind.paramArrayKeyword>;
    readonly optional?: DeclareKeywordToken<SyntaxKind.optionalKeyword>;
    readonly default?: AssignmentExpression;
}

export interface PropertySignature extends Signature<SyntaxKind.propertySignature> {
    readonly modifiers?: NodeArray<ModifierToken<DeclareModifierKind>>;
    readonly parameters?: NodeArray<ParameterSignature>;
    readonly bindingType: Binding;
}

export interface FunctionSignature extends Signature<SyntaxKind.functionSignature> {
    readonly parameters?: NodeArray<ParameterSignature>;
    readonly bindingType?: Binding;
}

export interface EnumMemberSignature extends Signature<SyntaxKind.enumMemberSignature> {
    readonly literal?: NumberLiteral;
    readonly value: number;
}

export interface EnumSignature extends Signature<SyntaxKind.enumSignature> {
    readonly members: NodeArray<EnumMemberSignature>;
    readonly module?: ModuleSignature;
}

export interface InheritLike extends Node {
    readonly kind: SyntaxKind.inheritLike;
    readonly fullName: NodeArray<Identifier>;
}

export interface InterfaceSignature extends Signature<SyntaxKind.interfaceSignature> {
    readonly kind: SyntaxKind.interfaceSignature;
    readonly inherits?: NodeArray<InheritLike>;
    readonly default?: PropertySignature;
    readonly properties: NodeArray<PropertySignature>;
    readonly methods: NodeArray<FunctionSignature>;
    readonly module?: ModuleSignature;
}

export interface ClassSignature extends Signature<SyntaxKind.classSignature> {
    readonly kind: SyntaxKind.classSignature;
    readonly default?: PropertySignature;
    readonly constants: NodeArray<ConstantDeclaration>;
    readonly properties: NodeArray<PropertySignature>;
    readonly methods: NodeArray<FunctionSignature>;
    readonly implements: NodeArray<InheritLike>;
    readonly inherits: NodeArray<InheritLike>;
    readonly module?: ModuleSignature;
}

export type ModuleMember = FunctionSignature | ClassSignature | InterfaceSignature | EnumSignature;

export interface ModuleSignature extends Signature<SyntaxKind.moduleSignature> {
    readonly kind: SyntaxKind.moduleSignature;
    readonly fullName: NodeArray<Identifier>;
    readonly functions: NodeArray<FunctionSignature>;
    readonly classes: NodeArray<ClassSignature>;
    readonly interfaces: NodeArray<InterfaceSignature>;
    readonly enums: NodeArray<EnumSignature>;
    readonly constants: NodeArray<ConstSignature>;
    readonly locals: SymbolTable;
}

export interface UnaryExpr extends Expression {
    readonly kind: SyntaxKind.unaryExpr;
    readonly operator: Token<UnaryOperatorKind>;
    readonly expression: Expression;
}

export interface BinaryExpression extends Expression {
    readonly kind: SyntaxKind.binaryExpr;
    readonly operator: Token<BinaryOperatorKind>;
    readonly left: Expression;
    readonly right: Expression;
}

export interface AssignmentExpression extends BinaryExpression {
    readonly operator: Token<SyntaxKind.equal>;
    readonly set?: KeywordToken<SyntaxKind.setKeyword>;
    _assignmentBrand: any;
}

export type MemberExpressionObject = MemberExpr | CallExpr | Identifier | IndexExpr;

export interface MemberExpr extends Expression {
    readonly kind: SyntaxKind.memberExpr;
    readonly object: MemberExpressionObject;
    readonly member: Identifier;
}

export enum IndexFlag {
    none                     = 1 << 0,
    string                   = 1 << 1,
    number                   = 1 << 2,
    identifier               = 1 << 3,
    expression               = 1 << 4,
    objectCollectionIterator = 1 << 5
}

export type IndexExprObject = MemberExpr | IndexExpr | Identifier;
export type IndexExprIndex = NumberLiteral | StringLiteral | ObjectCollectionIterator | Identifier | Expression;

export interface IndexExpr extends Expression {
    readonly kind: SyntaxKind.indexExpr;
    readonly object: IndexExprObject;
    readonly index: IndexExprIndex;
}

// object[.. <options>].method
// object[.. <options>].property = expression
// <options> ::= [In categories]
export interface ObjectCollectionIterator extends Expression {
    readonly kind: SyntaxKind.objectCollectionIterator;
    readonly dotDot: Token<SyntaxKind.dotDot>;
    readonly categories?: Identifier;
}

export type Callee = Identifier | MemberExpr;
export type Argument = Expression;

export interface CallExpr extends Expression {
    readonly kind: SyntaxKind.callExpr;
    readonly callee: Callee;
    readonly arguments: NodeArray<Argument>;
    readonly callByDot: boolean;
}

export const enum BuiltInMacroParameters {
    varArgs   = "__VA_ARGS__",
    date      = "__DATE__",
    file      = "__FILE__",
    line      = "__LINE__",
    stdc      = "__STDC__",
    time      = "__TIME__",
    timeStamp = "__TIMESTAMP__",
}

export type ParameterMacroArgument = NodeArray<Node> & {
    text: string;
};

export interface ParameterMacroCallExpr extends Expression {
    readonly kind: SyntaxKind.parameterMacroCallExpr;
    readonly callee: Identifier;
    readonly arguments: NodeArray<ParameterMacroArgument>;
}

export type StatementKind =
| DeclarationKind
| SignatureKind
| SectionKinds
| SyntaxKind.location
| SyntaxKind.localDecls
| SyntaxKind.exprStat
| SyntaxKind.block
| SyntaxKind.ifStat
| SyntaxKind.forStat
| SyntaxKind.forEachStat
| SyntaxKind.doStat
| SyntaxKind.whileStat
| SyntaxKind.selectCaseClause
| SyntaxKind.selectStat
| SyntaxKind.withStat
| SyntaxKind.sectionStat
| SyntaxKind.optionStat
| SyntaxKind.jumpStat
| SyntaxKind.onErrorStat
| SyntaxKind.resumeStat
| SyntaxKind.ppInclude
| SyntaxKind.ppDefine
| SyntaxKind.ppUndef
| SyntaxKind.ppIf
| SyntaxKind.ppIfDef
| SyntaxKind.ppIfNDef
| SyntaxKind.ppError
| SyntaxKind.ppLine;

export type SectionKinds =
| SyntaxKind.jobSection
| SyntaxKind.inputDataSourceSection
| SyntaxKind.outputDataSourceSection
| SyntaxKind.globalSQLVariablesSection
| SyntaxKind.metadataSection
| SyntaxKind.loggingSection
| SyntaxKind.eventSection
| SyntaxKind.routingSection
;

export interface Statement extends Node {
    readonly kind: StatementKind;
    _statementBrand: any;
}

export interface ExprStat extends Statement {
    readonly kind: SyntaxKind.exprStat;
    readonly expression: Expression;
}

export interface LocalDecls extends Statement {
    readonly kind: SyntaxKind.localDecls;
    readonly declarations: NodeArray<VariableDecl | ArrayDecl>;
};

export interface Block extends Statement, LocationBlockNode {
    readonly kind: SyntaxKind.block;
    readonly statements: NodeArray<Expression | Statement>;
}

// If  expression  Then
//   [statements]
// [ElseIf  expression-n Then
//   [elseifstatements]] ...
// [Else
//   [elsestatements]]
// End If
// If  expression  Then  statements  [Else  elsestatements]
export interface IfStat extends Statement {
    readonly kind: SyntaxKind.ifStat;
    readonly condition: Expression;
    readonly then: Statement | Expression;
    readonly else?: Statement | Expression;
    readonly elseKeyword?: Token<SyntaxKind.elseKeyword>;
    readonly singleLine: boolean;
}

// For counter = start To end [Step step] [{Ran|Rot|Rev} [it]]
//   [statements]
//   [Exit For]
//   [statements]
// Next
export interface ForStat extends Statement {
    readonly kind: SyntaxKind.forStat;
    readonly counter: Identifier;
    readonly range: IterationExpr;
    readonly step?: NumberLiteral;
    readonly statement: Statement;
}

// For Each element In group
//   [statements]
//   [Exit For]
//   [statements]
// Next
export interface ForEachStat extends Statement {
    readonly kind: SyntaxKind.forEachStat;
    readonly element: Identifier;
    readonly group: Expression | Identifier;
    readonly statement: Statement;
}

// While  expression
//   [statements]
// End While
export interface WhileStat extends Statement {
    readonly kind: SyntaxKind.whileStat;
    readonly expression: Expression;
    readonly statement: Statement;
}

// Do [{While | Until} expression]
//   [statements]
//   [Exit Do]
//   [statements]
// Loop
//
// Do
//   [statements]
//   [Exit Do]
//   [statements]
// Loop [{While | Until} expression]
export interface DoStat extends Statement {
    readonly kind: SyntaxKind.doStat;
    readonly expression: Expression;
    readonly until?: KeywordToken<SyntaxKind.untilKeyword>; // 是否是 do... until
    readonly preCondition: boolean;                  // expression是否紧跟do
    readonly statement: Statement;
}

export type SelectCaseCondition = NumberLiteral | StringLiteral | CategoricalLiteral | IterationExpr | SelectCaseComparison | Expression;

export type ComparisonPunctuationFlags = SyntaxKind.equal | SyntaxKind.greater | SyntaxKind.greaterEqual | SyntaxKind.less | SyntaxKind.lessEqual | SyntaxKind.notEqual;
export type ComparisonRightValue = NumberLiteral | StringLiteral | CategoricalLiteral | IterationExpr | Expression;

// > 1 / > "a" / > {1, 2, 3} / > Len(a) ...
export interface SelectCaseComparison extends Expression {
    readonly kind: SyntaxKind.selectCaseComparison;
    readonly operator: Token<ComparisonPunctuationFlags>;
    readonly right: ComparisonRightValue;
}

export interface SelectCaseClause extends Statement {
    readonly kind: SyntaxKind.selectCaseClause;
    readonly conditions: NodeArray<SelectCaseCondition>;
    readonly caseElse: boolean;
    readonly statement: Statement;
}

// Select Case  testexpression
//   [Case  expression-n
//     [statements-n]] ...
//   [Case Else
//     [elsestatements]]
// End Select
export interface SelectStat extends Statement {
    readonly kind: SyntaxKind.selectStat;
    readonly test: Expression;
    readonly cases: NodeArray<SelectCaseClause>;
}

// With objectexpression
//     statements
// End With
export interface WithStat extends Statement {
    readonly kind: SyntaxKind.withStat;
    readonly object: MemberExpressionObject;
    readonly statement: Statement;
}

// Section label
//   statements
// End Section
export interface SectionStat extends Declaration {
    readonly kind: SyntaxKind.sectionStat;
    readonly name: Identifier;
    readonly statement: Statement;
}

export enum SectionKind {
    none,
    job,
    inputDataSource,
    outputDataSource,
    globalSQLVariables,
    metadata,
    logging,
    event,
    routing,
}

export enum EventSectionKind {
    none                           = 1 << 0,
    onBeforeJobStart               = 1 << 1,
    onAfterMetaDataTransformation  = 1 << 2,
    onJobStart                     = 1 << 3,
    onNextCase                     = 1 << 4,
    onBadCase                      = 1 << 5,
    onJobEnd                       = 1 << 6,
    onAfterJobEnd                  = 1 << 7,
}

export const sectionMemberNames = {
    job: [ "TempDirectory" ],
    inputDataSource: [
        "ConnectionString",
        "SelectQuery",
        "UpdateQuery",
        "UseInputAsOutput",
        "JoinKey",
        "JoinType",
        "JoinKeySorted",
    ],
    outputDataSource: [
        "ConnectionString",
        "MetaDataOutputName",
        "UpdateQuery",
        "TableOutputName",
        "VariableOrder",
    ],
    globalSQLVariables: [
        "ConnectionString",
        "SelectQuery",
    ],
    logging: [
        "Group",
        "Path",
        "Alias",
        "FileSize",
    ],
};

export interface Section extends Declaration {
    readonly kind: SectionKinds;
    readonly sectionFlag: SectionKind;
}

// Job(name [, "description"])
//      [TempDirectory = "<Temp Directory>"]
// End Job
export interface JobSection extends Section {
    readonly kind: SyntaxKind.jobSection;
    readonly name: Identifier;
    readonly description?: StringLiteral;
    readonly tempDirectory?: AssignmentExpression;
}

// InputDataSource(name [", description"])
//    ConnectionString = "<connection_string>"
//    [SelectQuery = "<select_query>"]
//    [UpdateQuery = "<update_query>"]
//    [UseInputAsOutput = True | False"]
//    [JoinKey = "<variable_name>"]
//    [JoinType = "Full" | "Inner" | "Left"]
//    [JoinKeySorted = True | False"]
// End InputDataSource
export interface InputDataSourceSection extends Section {
    readonly kind: SyntaxKind.inputDataSourceSection;
    readonly name: Identifier;
    readonly description?: StringLiteral;
    readonly connectionString: AssignmentExpression;
    readonly selectQuery?: AssignmentExpression;
    readonly updateQuery?: AssignmentExpression;
    readonly useInputAsOutput?: AssignmentExpression;
    readonly joinKey?: AssignmentExpression;
    readonly joinType?: AssignmentExpression;
    readonly joinKeySorted?: AssignmentExpression;
}

// OutputDataSource(name [, "description"])
//    [ConnectionString = "<connection_string> | UseInputAsOutput = True|False"]
//    [MetaDataOutputName = "<metadata_location>"]
//    [UpdateQuery = "<update_query>"]
//    [TableOutputName = "<table_output_name>"]
//    [VariableOrder = "SELECTORDER"]
// End OutputDataSource
export interface OutputDataSourceSection extends Section {
    readonly kind: SyntaxKind.outputDataSourceSection;
    readonly name: Identifier;
    readonly description?: StringLiteral;
    readonly connectionString?: AssignmentExpression;
    readonly metadataDataOutputName?: AssignmentExpression;
    readonly updateQuery?: AssignmentExpression;
    readonly tableOutputName?: AssignmentExpression;
    readonly variableOrder?: AssignmentExpression;
}

// GlobalSQLVariables(name [, "description"])
//    ConnectionString = "<connection_string>"
//    SelectQuery = "<select_query>"
// End GlobalSQLVariables
export interface GlobalSQLVariablesSection extends Section {
    readonly kind: SyntaxKind.globalSQLVariablesSection;
    readonly name: Identifier;
    readonly description?: StringLiteral;
    readonly connectionString: AssignmentExpression;
    readonly selectQuery: AssignmentExpression;
}

// Metadata [(language [, context [, labeltype] [, datasource]])]
//       field_name "field description" <type_info>;
//       ...
// END Metadata
export interface MetadataSection extends Section {
    readonly kind: SyntaxKind.metadataSection;
    readonly language?: Expression;
    readonly context?: Expression;
    readonly labelType?: Expression;
    readonly dataSource?: Expression;
    readonly document: MDMDocument;
    readonly includes: readonly PPInclude[];
}

// Logging(name [, "description"])
//    Group = "Group"
//    Path = "Path"
//    Alias = "Alias"
//    [FileSize = FileSize
// End Logging
export interface LoggingSection extends Section {
    readonly kind: SyntaxKind.loggingSection;
    readonly name: Identifier;
    readonly description?: StringLiteral;
    readonly group: AssignmentExpression;
    readonly path: AssignmentExpression;
    readonly alias: AssignmentExpression;
    readonly fileSize?: AssignmentExpression;
}

// Event(name [, "description"])
//    ...
// End Event
export interface EventSection extends Section {
    readonly kind: SyntaxKind.eventSection;
    readonly name: Identifier;
    readonly eventSectionFlag: EventSectionKind;
    readonly description?: StringLiteral;
    readonly statement: Block;
    readonly functions: NodeArray<FunctionDeclaration>;
    readonly includes: readonly PPInclude[];
    readonly locals: SymbolTable;
    _eventSectionBrand: any;
}

// Routing(Context)
// ...
// End Routing
export interface RoutingSection extends Section {
    readonly kind: SyntaxKind.routingSection;
    readonly context: Identifier;
    readonly statement: Block;
    readonly functions: NodeArray<FunctionDeclaration>;
    readonly locals: SymbolTable;
    _routingSectionBrand: any;
}

export type PrecedureCanExit = SyntaxKind.forKeyword | SyntaxKind.doKeyword | SyntaxKind.whileKeyword | SyntaxKind.functionKeyword | SyntaxKind.subKeyword;
// Goto location
// On Error Goto location
// On Error Resume Next
// On Error Goto 0
// Exit
export interface JumpStatement extends Statement {
    readonly kind: SyntaxKind.jumpStat;
    readonly exit?: KeywordToken<SyntaxKind.exitKeyword>;
    readonly exitPrecedure?: KeywordToken<PrecedureCanExit>;
    readonly goto?: KeywordToken<SyntaxKind.gotoKeyword>;
    readonly location?: Identifier | NumberLiteral;
    readonly on?: KeywordToken<SyntaxKind.onKeyword>;
    readonly error?: KeywordToken<SyntaxKind.errorKeyword>;
    readonly resume?: ResumeStatement;
}

export interface ResumeStatement extends Statement {
    readonly kind: SyntaxKind.resumeStat;
    readonly resume: KeywordToken<SyntaxKind.resumeKeyword>;
    readonly next: KeywordToken<SyntaxKind.nextKeyword>;
}

export type OptionSettingKeyword =
| KeywordToken<SyntaxKind.implicitKeyword>
| KeywordToken<SyntaxKind.explicitKeyword>
| KeywordToken<SyntaxKind.globalVariablesKeyword>
;

// Option [implicit|explicit|globalvariables]
export interface OptionStatement extends Statement {
    readonly kind: SyntaxKind.optionStat;
    readonly option: OptionSettingKeyword;
    readonly optionFlag: OptionFlag;
}

//
//  Pre-Processor
//

export type PPStatementKind =
| SyntaxKind.ppDefine
| SyntaxKind.ppUndef
| SyntaxKind.ppIf
| SyntaxKind.ppIfDef
| SyntaxKind.ppIfNDef
| SyntaxKind.ppInclude
| SyntaxKind.ppError
| SyntaxKind.ppLine
;

export interface Preprocessor extends Statement {
    readonly kind: PPStatementKind;
    _preprocessorBrand: any;
}

// #define identifier [ ["] value ["] ]
// #define identifier(parameters, ..) definition(parameters, , ..)
// + `#` - 将参数内容转换为字符串，当传入任意内容都不应该报错
// + `##` - 将两个标识符合并为一个标识符
// 注意
// + 预处理指令的错误检查需要在替换后执行。
// + 预处理参数未使用的话不应该报错。
// + `##`操作符的左右值可以都不是参数。

export interface PPDefinitionOperation extends Node {
    readonly kind: SyntaxKind.ppDefineStringizer | SyntaxKind.ppDefineTokenPaster;
}

// #identifier -> "identifier"
export interface PPDefineStringizer extends PPDefinitionOperation {
    readonly kind: SyntaxKind.ppDefineStringizer;
    readonly name: Identifier;
}

// left##right -> leftright (左右值组成新的标识符)
export interface PPDefineTokenPaster extends PPDefinitionOperation {
    readonly kind: SyntaxKind.ppDefineTokenPaster;
    readonly identifiers: NodeArray<Identifier>;
}

export interface PPDefineParameter extends Node {
    readonly kind: SyntaxKind.ppDefineParameter;
    readonly name: Identifier;
    readonly isStringizer: boolean;
    readonly isTokenPaster: boolean;
    readonly dotDotDot?: Token<SyntaxKind.dotDotDot>;
}

export interface PPDefine extends Preprocessor {
    readonly kind: SyntaxKind.ppDefine;
    readonly name: Identifier;
    readonly parameters?: NodeArray<PPDefineParameter>;
    readonly definition?: NodeArray<Node>;
    readonly valueText: string;
}

// #undef identifier
export interface PPUndef extends Preprocessor {
    readonly kind: SyntaxKind.ppUndef;
    readonly name: Identifier;
}

// #include "filename"
export interface PPInclude extends Preprocessor {
    readonly kind: SyntaxKind.ppInclude;
    readonly path: StringLiteral | Expression;
    readonly resolvedPath: string;
    readonly targetFile?: SourceFile;
}

// #if expression
//   [statements]
// [#elif expression-n
//   [elifstatements-n]] ...
// [#else
//   [elsestatements]]
// #endif
export interface PPIf extends Preprocessor {
    readonly kind: SyntaxKind.ppIf;
    readonly condition: Expression;
    readonly then: Statement;
    readonly else?: Statement;
    readonly elseKeyword?: Token<SyntaxKind.ppElseKeyword>;
}

// #ifdef macro_name
//    [statements]
// [#elif expression-n]...
export interface PPIfDef extends Preprocessor {
    readonly kind: SyntaxKind.ppIfDef;
    readonly macroName: Identifier;
    readonly then: Statement;
    readonly else?: Statement;
    readonly elseKeyword?: Token<SyntaxKind.ppElseKeyword>;
}

// #ifndef macro_name
//    [statements]
// [#elif expression-n]...
export interface PPIfNDef extends Preprocessor {
    readonly kind: SyntaxKind.ppIfNDef;
    readonly macroName: Identifier;
    readonly then: Statement;
    readonly else?: Statement;
    readonly elseKeyword?: Token<SyntaxKind.ppElseKeyword>;
}

// #error text
export interface PPError extends Preprocessor {
    readonly kind: SyntaxKind.ppError;
    readonly text: string;
}

// #line sequence ["location"]
export interface PPLine extends Preprocessor {
    readonly kind: SyntaxKind.ppLine;
    readonly sequence: NumberLiteral;
    readonly location?: StringLiteral;
}


// Metadata

//export interface MetadataLanguage {
//    readonly shortCode: string;
//    readonly longCode: string;
//    readonly label: string;
//}

export type MDMTokenKind =
| SyntaxKind.mdmLabel
| SyntaxKind.mdmValueRange
| SyntaxKind.mdmDocument
| MDMDeclarationKind
;

export enum MDMUserContextFlags {
    analysis,
    question,
    qc,
    paper,
    cardcol,
    sav,
    webapp,
    web,
    cati,
    mobile,
    papi,
}

export enum MDMLabelTypeFlags {
    label,
    instruction,
    shortName,
}

export enum MDMPropertyFlags {
    align,
    bgColor,
    cell,
    color,
    columns,
    control,
    cursor,
    elementAlign,
    font,
    height,
    hidden,
    image,
    imagePosition,
    indent,
    orientation,
    rows,
    verticalAlign,
    width,
    zIndex,
}

export enum MDMCellStylePropertyFlags {
    bgColor,
    borderBottomColor,
    borderBottomStyle,
    borderBottomWidth,
    borderColor,
    borderLeftColor,
    borderLeftStyle,
    borderLeftWidth,
    borderRightColor,
    borderRightStyle,
    borderRightWidth,
    borderStyle,
    borderTopColor,
    borderTopStyle,
    borderTopWidth,
    borderWidth,
    colspan,
    height,
    padding,
    paddingBottom,
    paddingLeft,
    paddingRight,
    paddingTop,
    repeatHeader,
    repeatSideHeader,
    rowspan,
    width,
    wrap,
}

export enum MDMControlStylePropertyFlags {
    accelerator,
    readonly,
    type,
}

export enum MDMFontStylePropertyFlags {
    family,
    isBlink,
    isBold,
    isItalic,
    isOverline,
    isStrikeThrough,
    isSubscript,
    isSuperscript,
    isUnderline,
    size,
}

export const DEFAULT_CONTEXT = MDMUserContextFlags.analysis;
export const DEFAULT_LABEL_TYPE = MDMLabelTypeFlags.label;

export interface MDMObject extends Node {
    readonly kind: MDMTokenKind;
}

export interface MDMDeclaration extends Declaration {
    readonly kind: MDMDeclarationKind;
}

// AreaName lcl ( [language] [, [user-context] [, [label-type] ]] )
export interface MDMAreaName extends MDMDeclaration {
    readonly kind: SyntaxKind.mdmAreaName;
    readonly areaName: Identifier;
    readonly language?: Expression;
    readonly context?: Expression;
    readonly userContextFlag: MDMUserContextFlags;
    readonly labelType?: Expression;
    readonly labelTypeFlag: MDMLabelTypeFlags;
}

export interface MDMArealBase extends MDMObject {
    readonly areaName?: Identifier;
}

export interface MDMConflictCallback<T> {
    (exist: T, newObject: T): void;
}

// [ AreaName: ]
export interface MDMArealLike<T extends MDMArealBase> extends MDMArealBase {
    readonly areaObjects: ReadonlyMap<string, T>;
    areaObjectCount(): number;
    forEachObject(callback: (obj: T, area: string) => void): void;
    addArealObject(items: T[], replaceWhenExist?: true, conflictCallback?: MDMConflictCallback<T>): void;
    addArealObject(item: T, replaceWhenExist?: true, conflictCallback?: MDMConflictCallback<T>): void;
    merge(newObjects: MDMArealLike<T>, replaceWhenExist?: true, conflictCallback?: MDMConflictCallback<T>): void;
}

// Sample
//
// LT_Instr    lcl ( , ,Instruction);
// L_ES        lcl (ESN, , );
// UC_Analysis lcl ( , Analysis, );
//
// Gender2 "Gender?"
//     LT_Instr:    "DO NOT ASK"
//     L_ES:        "El gnero"
//     UC_Analysis: "Gender"
//     categorical [1]
//         {
//             male "Male"
//                 L_ES: "Masculino",
//             female "Female"
//                 L_ES: "La hembra"
//         };

// <label> ::= ( [ AreaName: ] ( "Label-text" | - ) )*
export interface MDMLabel extends MDMArealLike<MDMLabel> {
    readonly kind: SyntaxKind.mdmLabel;
    readonly label: StringLiteral | Token<SyntaxKind.minus>;
}

export interface MDMKeyValuePair extends MDMObject {
    readonly name: Identifier;
    readonly value: Literal | Identifier; // metadata内容解析时，允许自动将Identifier转换成字符串
}

// <property> ::= Name = Value
export interface MDMProperty extends MDMDeclaration, MDMKeyValuePair {
    readonly kind: SyntaxKind.mdmProperty;
}

export interface MDMPropertiesLike<T extends MDMKeyValuePair, U extends MDMDeclaration> extends MDMArealLike<U> {
    readonly kind: U["kind"];
    readonly items: ReadonlyMap<string, T>;
    itemsCount(): number;
    forEachItem(callback: (item: T) => void): void;
    getItem(name: string): T | undefined;
    addItem(item: T, replaceWhenExist?: true, conflictCallback?: MDMConflictCallback<T>): void;
    addItems(items: readonly T[], replaceWhenExist?: true, conflictCallback?: MDMConflictCallback<T>): void;
    addItems(items: ReadonlyMap<string, T>, replaceWhenExist?: true, conflictCallback?: MDMConflictCallback<T>): void;
}

// <Properties> ::= ( [ AreaName: ] [ <property> (, <property> )* ] )*
export interface MDMProperties extends MDMDeclaration, MDMPropertiesLike<MDMProperty, MDMProperties> {
    readonly kind: SyntaxKind.mdmProperties;
}

// To define styles and the layout template for the document,
// create an info field called HDATA and add the style and templates keywords to that field:
//
// <field> ::= HDATA
//             ( [ AreaName: ] style ( <property> (, <property> )* ) )*
//             ( [ AreaName: ] templates ( layout = "filename" ) )*
//             info

// field_item = "filename"
// 如果template配置值是空字符串或是数字、Null、True、False，应该忽略此项配置
export interface MDMTemplate extends MDMDeclaration, MDMKeyValuePair {
    readonly kind: SyntaxKind.mdmTemplate;
}

// <Templates> ::= ( [ AreaName: ] templates ( field_item = "filename" (, field_item = "filename" )* ) )*
export interface MDMTemplates extends MDMDeclaration, MDMPropertiesLike<MDMTemplate, MDMTemplates> {
    readonly kind: SyntaxKind.mdmTemplates;
}

// Style 配置，允许同一个Key重复设置，只保留第一个配置值，忽略后面的配置

// Type = "Password",
// ReadOnly = false,
// Accelerator = "R"

export interface MDMControlStyleProperties {
    readonly type?: MDMProperty;
    readonly readOnly?: MDMProperty;
    readonly accelerator?: MDMProperty;
}

export interface MDMControlStyle extends MDMDeclaration, MDMControlStyleProperties {
    readonly kind: SyntaxKind.mdmControlStyle;
}

// Family
// Size
// IsUnderline
// IsItalic
// IsBold
// IsStrikethrough
// IsOverline
// IsBlink
// IsSuperscript
// IsSubscript

export interface MDMFontStyleProperties {
    readonly family?: MDMProperty;
    readonly size?: MDMProperty;
    readonly isUnderline?: MDMProperty;
    readonly isItalic?: MDMProperty;
    readonly isBold?: MDMProperty;
    readonly isStrikethrough?: MDMProperty;
    readonly isOverline?: MDMProperty;
    readonly isBlink?: MDMProperty;
    readonly isSuperscript?: MDMProperty;
    readonly isSubscript?: MDMProperty;
}

export interface MDMFontStyle extends MDMDeclaration, MDMFontStyleProperties {
    readonly kind: SyntaxKind.mdmFontStyle;
}

// Width = "2",
// Height = "1 px",
// BorderColor = "blue",
// BorderTopColor = "yellow",
// BorderRightColor = "white",
// BorderBottomColor = "blue",
// BorderLeftColor = "black",
// BorderStyle = "Double",
// BorderWidth = 11,
// BorderTopWidth = 10,
// BorderRightWidth = 12,
// BorderBottomWidth = 1,
// BorderLeftWidth = 23,
// BorderTopStyle = "Inset",
// BorderRightStyle = "Ridge",
// BorderBottomStyle = "Double",
// BorderLeftStyle = "Solid",
// Padding = 2,
// PaddingTop = 1,
// PaddingRight = 2,
// PaddingBottom = 1,
// PaddingLeft = 2,
// Wrap = false,
// ColSpan = 1,
// RowSpan = 1,
// BgColor = "blue",
// RepeatHeader = 0

export interface MDMCellStyleProperties {
    readonly width?: MDMProperty;
    readonly height?: MDMProperty;
    readonly borderColor?: MDMProperty;
    readonly borderTopColor?: MDMProperty;
    readonly borderRightColor?: MDMProperty;
    readonly borderBottomColor?: MDMProperty;
    readonly borderLeftColor?: MDMProperty;
    readonly borderStyle?: MDMProperty;
    readonly borderWidth?: MDMProperty;
    readonly borderTopWidth?: MDMProperty;
    readonly borderRightWidth?: MDMProperty;
    readonly borderBottomWidth?: MDMProperty;
    readonly borderLeftWidth?: MDMProperty;
    readonly borderTopStyle?: MDMProperty;
    readonly borderRightStyle?: MDMProperty;
    readonly borderBottomStyle?: MDMProperty;
    readonly borderLeftStyle?: MDMProperty;
    readonly padding?: MDMProperty;
    readonly paddingTop?: MDMProperty;
    readonly paddingRight?: MDMProperty;
    readonly paddingBottom?: MDMProperty;
    readonly paddingLeft?: MDMProperty;
    readonly wrap?: MDMProperty;
    readonly colSpan?: MDMProperty;
    readonly rowSpan?: MDMProperty;
    readonly bgColor?: MDMProperty;
    readonly repeatHeader?: MDMProperty;
    readonly repeatSideHeader?: MDMProperty;
}

export interface MDMCellStyle extends MDMDeclaration, MDMCellStyleProperties {
    readonly kind: SyntaxKind.mdmCellStyle;
}

// style(
//     Color = "Blue",
//     BgColor = "blue",
//     Hidden = false,
//     Align = "Center",
//     VerticalAlign = "Super",
//     ElementAlign = "NewLine",
//     Indent = 3,
//     ZIndex = 3,
//     Cursor = "SEResize",
//     ImagePosition = "Left",
//     Orientation = "Row",
//     Columns = 2,
//     Rows = 3,
//     Width = "4in",
//     Height = "23%",
//     Control(
//         Type = "Password",
//         ReadOnly = false,
//         Accelerator = "R"
//     ),
//     Cell(
//         Width = "2",
//         Height = "1. 234 PX",
//         BorderColor = "blue",
//         BorderTopColor = "yellow",
//         BorderRightColor = "white",
//         BorderBottomColor = "blue",
//         BorderLeftColor = "black",
//         BorderStyle = "Double",
//         BorderWidth = 11,
//         BorderTopWidth = 10,
//         BorderRightWidth = 12,
//         BorderBottomWidth = 1,
//         BorderLeftWidth = 23,
//         BorderTopStyle = "Inset",
//         BorderRightStyle = "Ridge",
//         BorderBottomStyle = "Double",
//         BorderLeftStyle = "Solid",
//         Padding = 2,
//         PaddingTop = 1,
//         PaddingRight = 2,
//         PaddingBottom = 1,
//         PaddingLeft = 2,
//         Wrap = false,
//         ColSpan = 1,
//         RowSpan = 1,
//         BgColor = "blue",
//         RepeatHeader = 0
//     ),
//     Font(
//         Family = "arial",
//         Size = 3,
//         IsUnderline = true,
//         IsItalic = false,
//         IsBold = true,
//         IsStrikethrough = true,
//         IsOverline = false,
//         IsBlink = true,
//         IsSuperscript = false,
//         IsSubscript = false
//     )
// ),
// <Styles> ::= ( [ AreaName: ] style ( <property> (, <property> )* ) )*

export interface MDMStyleProperties {
    readonly color?: MDMProperty;
    readonly bgColor?: MDMProperty;
    readonly hidden?: MDMProperty;
    readonly align?: MDMProperty;
    readonly verticalAlign?: MDMProperty;
    readonly elementAlign?: MDMProperty;
    readonly indent?: MDMProperty;
    readonly zIndex?: MDMProperty;
    readonly cursor?: MDMProperty;
    readonly image?: MDMProperty;
    readonly imagePosition?: MDMProperty;
    readonly orientation?: MDMProperty;
    readonly columns?: MDMProperty;
    readonly rows?: MDMProperty;
    readonly width?: MDMProperty;
    readonly height?: MDMProperty;
}

export interface MDMStyle extends MDMDeclaration, MDMArealLike<MDMStyle>, MDMStyleProperties {
    readonly kind: SyntaxKind.mdmStyle;
    readonly control?: MDMControlStyle;
    readonly cell?: MDMCellStyle;
    readonly font?: MDMFontStyle;
    set(property: [ keyof MDMStyleProperties, MDMProperty ]): void;
    set(control?: MDMControlStyle, cell?: MDMCellStyle, font?: MDMFontStyle): void;
    set(object: MDMStyleProperties): void;
    set(object: MDMStyle): void;
}

export interface MDMNamedObject extends MDMDeclaration {
    readonly name: Identifier;
}

export interface MDMLabeledObject extends MDMNamedObject {
    readonly properties?: MDMProperties;
    readonly templates?: MDMTemplates;
    readonly label?: MDMLabel;
    readonly style?: MDMStyle;
    readonly labelStyle?: MDMStyle;
}

//  Value           Applies to Fields of Type
//
//  Filter          Boolean
//  Weight          Numeric
//  Multiplier      Numeric
//  Coding          Categorical
//  SourceFile      Text
//  OtherSpecify    Text
//  HelperField     Any type
//

export enum MDMUsageTypeFlags {
    none         = 0,
    filter       = 1 << 0,
    weight       = 1 << 1,
    multiplier   = 1 << 2,
    coding       = 1 << 3,
    sourceFile   = 1 << 4,
    otherSpecify = 1 << 5,
    helperField  = 1 << 6
}

// usagetype ( "<usage-type-value> (, <usage-type-value> )* " )
export interface MDMUsageType extends MDMDeclarationValueLike<SyntaxKind.mdmUsageType, StringLiteral> {
    readonly typeFlag: MDMUsageTypeFlags;
}

// helperfields ( <field> (; <field> )* [;] )
export interface MDMHelperFields extends MDMDeclaration {
    readonly kind: SyntaxKind.mdmHelperFields;
    readonly fields: NodeArray<MDMField>;
}

export interface MDMSortOrder {
    readonly rotate?: Token<SyntaxKind.mdmRotKeyword> | Token<SyntaxKind.mdmRotateKeyword>;
    readonly randomize?: Token<SyntaxKind.mdmRanKeyword> | Token<SyntaxKind.mdmRandomizeKeyword>;
    readonly reverse?: Token<SyntaxKind.mdmRevKeyword> | Token<SyntaxKind.mdmReverseKeyword>;
    readonly ascending?: Token<SyntaxKind.mdmAscKeyword> | Token<SyntaxKind.mdmAscendingKeyword>;
    readonly descending?: Token<SyntaxKind.mdmDescKeyword> | Token<SyntaxKind.mdmDescendingKeyword>;
    readonly fix?: Token<SyntaxKind.mdmFixKeyword>;
}

export enum MDMCategoryElementTypeFlags {
    analysisBase,
    analysisCategory,
    analysisMaximum,
    analysisMean,
    analysisMinimum,
    analysisSampleVariance,
    analysisStdDev,
    analysisStdErr,
    analysisSubHeading,
    analysisSubTotal,
    analysisSummaryData,
    analysisTotal
}

export interface MDMCategoryElementType extends MDMDeclaration {
    readonly kind: SyntaxKind.mdmElementType;
    readonly elementType: Identifier | StringLiteral;
    readonly elementTypeFlag: MDMCategoryElementTypeFlags;
}

export interface MDMDeclarationValueLike<F extends MDMDeclarationKind, T> extends MDMDeclaration {
    readonly kind: F;
    readonly value: T;
}

export interface MDMCategoryExpression extends MDMDeclarationValueLike<SyntaxKind.mdmCategoryExpression, StringLiteral> {
    readonly deriveElements?: Token<SyntaxKind.mdmDeriveElementsKeyword>;
    readonly noDeriveElements?: Token<SyntaxKind.mdmNoDeriveElementsKeyword>;
}

export interface MDMKeycode extends MDMDeclarationValueLike<SyntaxKind.mdmKeycode, StringLiteral> {
}

export interface MDMFactor extends MDMDeclarationValueLike<SyntaxKind.mdmFactor, NumberLiteral | TrueLiteral | FalseLiteral | StringLiteral | NullLiteral> {
}

export interface MDMMultiplier extends MDMDeclaration {
    readonly kind: SyntaxKind.mdmMultiplier;
    readonly field?: StringLiteral | MDMField;
}

export interface MDMOther extends MDMDeclaration {
    readonly kind: SyntaxKind.mdmOther;
    readonly field?: StringLiteral | MDMField;
}

export interface MDMSublist extends MDMLabeledObject, MDMSortOrder {
    readonly kind: SyntaxKind.mdmSublist;
}

export interface MDMCollection<T extends MDMLabeledObject> extends MDMObject {
    _metadataCollectionBrand: any;
    readonly count: number;
    readonly elements: readonly T[];
    forEachElement(callback: (element: T, index: number) => void): void;
    getElement(name: string): T | undefined;
    getElementAt(index: number): T;
    removeElement(name: string): void;
    removeElementAt(index: number): void;
    add(element: T): void;
    add(element: T[]): void;
}

// <category> ::= category_name
//                [ "category_label" ]
//                [ <other> | <multiplier> | DK | REF | NA ]
//                [ exclusive ]
//                [ factor (factor_value) ]
//                [ keycode ("keycode_value") ]
//                [ expression ("exp_text") ]
//                [ elementtype (type_value) ]
//                [ fix ]
//                [ nofilter ]

// <other> ::= other [ ( (use "helper_field_name")
//                        | (helper_field_name "label" variable_type) ) ]

// <multiplier> ::= multiplier ( (use "helper_field_name")
//                                | (helper_field_name "label" variable_type) )

// <category> ::= [ list_name ]
//            [ "list_label" ]
//            <categories>

// <category> ::= [ list_name ]
//            use define_list
//            [ sublist [ rot[ate] | ran[domize] | rev[erse] |
//                asc[ending] | desc[ending] ] ]
//            [ "list_label" ]
//            [ fix ]

// <categories> ::= { <category> (, <category>)* }
//                  [ rot[ate] | ran[domize] | rev[erse] |
//                      asc[ending] | desc[ending] ]
//                  [ fix ]
//                  [ namespace ]

export interface MDMCategory extends MDMLabeledObject, MDMSortOrder {
    readonly kind: SyntaxKind.mdmCategory;
    readonly name: Identifier;
    readonly label?: MDMLabel;
    readonly multiplier?: MDMMultiplier;
    readonly other?: MDMOther;
    readonly dk?: Token<SyntaxKind.mdmDKKeyword>;
    readonly ref?: Token<SyntaxKind.mdmREFKeyword>;
    readonly na?: Token<SyntaxKind.mdmNAKeyword>;
    readonly exclusive?: Token<SyntaxKind.mdmExclusiveKeyword>;
    readonly factor?: MDMFactor;  //  You can use the optional factor keyword to set a factor value for a category. Factors are numeric values that are used in statistical calculations. You can also set a factor value for the special response categories, DK, REF and NA.
    readonly keycode?: MDMKeycode;
    readonly expression?: MDMCategoryExpression;
    readonly elementType?: MDMCategoryElementType;
    readonly canfilter?: Token<SyntaxKind.mdmCanFilterKeyword>;
    readonly nofilter?: Token<SyntaxKind.mdmNoFilterKeyword>;
    readonly backslashBackslashDot?: Token<SyntaxKind.backslashBackslashDot>;
    readonly listName?: Identifier;
    readonly list?: Identifier;
    readonly sublist?: MDMSublist;
    readonly categories?: MDMCategories;
    readonly namespace?: Token<SyntaxKind.mdmNamespaceKeyword>;
}

export interface MDMCategories extends MDMObject, MDMCollection<MDMCategory>, MDMSortOrder {
    readonly kind: SyntaxKind.mdmCategories;
    readonly namespace?: Token<SyntaxKind.mdmNamespaceKeyword>;
}

// field_name
// [ "field_label" ]
// [ [ <properties> ] ]
// [ <styles and templates> ]
export interface MDMFieldDefinitionHeader {
    readonly name: Identifier;
    readonly label?: MDMLabel;
    readonly properties?: MDMProperties;
    readonly templates?: MDMTemplates;
    readonly style?: MDMStyle;
    readonly labelStyle?: MDMStyle;
}

export interface MDMPrecision extends MDMDeclarationValueLike<SyntaxKind.mdmPrecision, NumberLiteral> {
}

export interface MDMScale extends MDMDeclarationValueLike<SyntaxKind.mdmScale, NumberLiteral> {
}

export interface MDMCodes extends MDMDeclarationValueLike<SyntaxKind.mdmCodes, NodeArray<MDMCategory>> {
    readonly useField?: Identifier;
}

export interface MDMValidation extends MDMDeclarationValueLike<SyntaxKind.mdmValidation, StringLiteral> {
}

export interface MDMInitialAnswer extends MDMDeclarationValueLike<SyntaxKind.mdmInitialAnswer, Literal | Identifier> {
}

export interface MDMDefaultAnswer extends MDMDeclarationValueLike<SyntaxKind.mdmDefaultAnswer, Literal | Identifier> {
}

export interface MDMAxis extends MDMDeclarationValueLike<SyntaxKind.mdmAxis, StringLiteral> {
}

// [ precision (integer) ]
// [ <categories> ]
// [ <codes> ]
// [ validation ("validation_text") ]
// [ expression ("expression_text") ]
// [ ( initialanswer | defaultanswer ) (text) ]
// [ <axis> ]
// [ <usage-type> ]
// [ <helperfields> ]
// [ nocasedata ]
// [ unversioned ]
export interface MDMFieldDefinitionTail {
    readonly precision?: MDMPrecision;
    readonly scale?: MDMScale;
    readonly codes?: MDMCodes;
    readonly validation?: MDMValidation;
    readonly expression?: MDMCategoryExpression;
    readonly initialAnswer?: MDMInitialAnswer;
    readonly defaultAnswer?: MDMDefaultAnswer;
    readonly axis?: MDMAxis;
    readonly usageType?: MDMUsageType;
    readonly helperFields?: MDMHelperFields;
    readonly noCaseData?: Token<SyntaxKind.mdmNocaseDataKeyword>;
    readonly unversioned?: Token<SyntaxKind.mdmUnversionedKeyword>;
}

// Field long;
// Field long [n1..n2];                      - Visits long [1..99];
// Field long [n1..];                        - People long [1..];
// Field long [..n1];                        - Cars long [..10];
// Field long [n1, n2, n3, n4 ...];          - Size long [100, 200, 500];
// Field long [n1..n2, n3..n4, n5..n6 ...];  - Rate1 long [24..56, 100..1000];
// Field long [^n1..n2];                     - Grade long [1..10, ^5..7];
// Field long [n1..n2 step n3];              - DressSize "Dress size" long [6..30 step 2];
export interface MDMValueRange extends MDMObject {
    readonly kind: SyntaxKind.mdmValueRange;
    readonly step?: NumberLiteral;
    readonly lowerBoundary?: NumberLiteral | StringLiteral;
    readonly upperBoundary?: NumberLiteral | StringLiteral;
    readonly exclude?: Token<SyntaxKind.caret>;
    readonly dotDot?: Token<SyntaxKind.dotDot>;
}

export enum MDMFieldFlags {
    unknown,
    long,
    double,
    text,
    date,
    boolean,
    categorical,
    define,
    info,
    categoricalLoop,
    numericLoop,
    grid,
    compound,
    block,
    page,
    databaseQuestions,
}

export interface MDMHierarchicalView extends MDMLabeledObject {
    readonly kind: SyntaxKind.mdmHierarchicalView;
}

// field_name
//     [ "field_label" ]
//     [ [ <properties> ] ]
//     [ <styles and templates> ]
// loop
// <categories>
// fields
//     [ "iteration_label" ]
// ( <field> (; <field> )* [;] )
//     [ row | column ]
//     [ expand ]
//     [ grid ]

export interface MDMClass extends MDMLabeledObject {
    readonly types: NodeArray<MDMField>;
    readonly fields: NodeArray<MDMField>;
    readonly pages: NodeArray<MDMField>;
}

export interface MDMField extends MDMLabeledObject, MDMFieldDefinitionHeader, MDMFieldDefinitionTail {
    readonly kind: SyntaxKind.mdmField;
    readonly fieldFlag: MDMFieldFlags;
    readonly fieldFlagToken?: Token<MDMFieldTokenKind>;
    readonly valueRange?: NodeArray<MDMValueRange>;
    readonly categories: MDMCategories;
    readonly class?: MDMClass;
    readonly row?: Token<SyntaxKind.mdmRowKeyword>;
    readonly column?: Token<SyntaxKind.mdmColumnKeyword>;
    readonly expand?: Token<SyntaxKind.mdmExpandKeyword>;
    readonly grid?: Token<SyntaxKind.mdmGridKeyword>;
    readonly parent: MDMField | MDMDocument;
}

export interface MDMDbQuestionsColumn {
    readonly columnId?: MDMProperty;
    readonly columnLabel?: MDMProperty;
    readonly columnKeycode?: MDMProperty;
    readonly columnFile?: MDMProperty;
    readonly columnAnalysisValue?: MDMProperty;
    readonly columnFixed?: MDMProperty;
    readonly columnExclusive?: MDMProperty;
}

// field_name
//     [ "field_label" ]
//     [ [ <properties> ] ]
//     [ <styles and templates> ]
// [text | double | long | date]
// db
// (
//         ConnectionString = "connection_string_value",
//         Table = "table_value",
//         [MinAnswers = min_answers_value]
//         [MaxAnswers = max_answers_value]
//         [SQLFilter = sqlfilter_string]
//         [CacheTimeout = cache_timeout_value]
//         Columns
//         (
//         ID = "id_column_name",
//         Label = "label_column_name"
//         [KeyCode = "keycode_column_name"]
//         [File = "file_column_name"]
//         [AnalysisValue = "analysis_value_column_name"]
//         [Fixed = "fixed_column_name"]
//         [Exclusive = "exclusive_column_name"]
//         )
//     )
//     [ [ range_expression ] ]
//     [ <codes> ]
//     [ expression ("expression_text") ]
//     [ ( initialanswer | defaultanswer ) (date_value) ]
//     [ <axis> ]
//     [ <usage-type> ]
//     [ <helperfields> ]
//     [ nocasedata ]
//     [ unversioned ]

// field_name
//     [ "field_label" ]
//     [ [ <properties> ] ]
//     [ <styles and templates> ]
// loop
//     [ range_expression ]
// db
// (
//         ConnectionString = "connection_string_value",
//         Table = "table_value",
//         [SQLFilter = sqlfilter_string]
//         [CacheTimeout = cache_timeout_value]
//         [IteratorIDType = [text | double | long | date]]
//         Columns
//         (
// 	            ID = "id_column_name",
// 	            Label = "label_column_name"
// 	            [KeyCode = "keycode_column_name"]
// 	            [File = "file_column_name"]
// 	            [AnalysisValue = "analysis_value_column_name"]
// 	            [Fixed = "fixed_column_name"]
// 	            [Exclusive = "exclusive_column_name"]
//         )
// )
// fields
// ( <field> (; <field> )* [;] )
//     [ row | column ]
//     [ noexpand ]

export interface MDMDbQuestionsProperties {
    readonly connectionString?: MDMProperty;
    readonly table?: MDMProperty;
    readonly minAnswers?: MDMProperty;
    readonly maxAnswers?: MDMProperty;
    readonly sqlFilter?: MDMProperty;
    readonly cacheTimeout?: MDMProperty;
    readonly iteratorIDType?: MDMProperty;
    readonly columns?: MDMDbQuestionsColumn;
}

export interface MDMDbQuestions extends MDMField, MDMDbQuestionsProperties {
    readonly dbType?: MDMFieldFlags;
    readonly noExpand?: Token<SyntaxKind.mdmNoExpandKeyword>;
    _metadataDBBrand: any;
}

// field_name
//     [ "field_label" ]
//     [ [ <properties> ] ]
//     [ <styles and templates> ]
// page
// ( question_name (, question_name)* )

export interface MDMPage extends MDMField {
    readonly questionNames: NodeArray<Identifier>;
    _metadataPageBrand: any;
}


export interface MDMDocument extends MDMObject {
    readonly kind: SyntaxKind.mdmDocument;
    readonly language: LocalLanguage;
    readonly context: MDMUserContextFlags,
    readonly labelType: MDMLabelTypeFlags;
    readonly types: NodeArray<MDMField>;
    readonly fields: NodeArray<MDMField>;
    readonly hdata?: MDMHierarchicalView;
    readonly lcl: NodeArray<MDMAreaName>;
    readonly preprocessor?: NodeArray<Preprocessor>;
    readonly locals: SymbolTable;
}

/**
 * 此类表示在批处理文件内定义宏。可能是mrScriptCL中的/d:MACRO=VALUE或者在DMSRun中的/d"\"...\"
 */
export interface BatchMacro {
    /**
     * Bacth文件完整路径
     */
    fileName: string;
    /**
     * 定义的宏名称。
     */
    name: string;
    /**
     * 宏值类型。
     */
    type: "number" | "boolean" | "string" | "other",
    /**
     * 宏定义内容。
     */
    value: string;
    /**
     * 定义起始位置。
     */
    start: number;
    /**
     * 定义结束位置。
     */
    end: number;
}

export interface SearchProgramFileCallback {
    (path: string): File | undefined;
}

/**
 * 用于标记使用 `#include MACRO_NAME` 时的文件引用位置，
 * 识别格式为 `<reference file="" [name=""] />`
 */
export interface FileDirective {
    /**
     * 引用的文件名。需注意，此属性不是当前源文件的文件名，而是引用标记指向的文件名。
     * 并且此处文件名应当为完整路径。
     */
    readonly fileName: string;
    /**
     * 引用文件处的宏值，应当符合脚本标识符的文本规则。
     */
    readonly value: string;
    /**
     * 引用标记的文本位置。
     */
    readonly span: TextSpan;
}

/**
 * 此对象表示文件引用关系。
 */
export interface FileReference {
    /**
     * 引用的文件对象。
     */
    readonly file: File;
    /**
     * 指向引用的文件的引用标记对象。
     */
    readonly directive?: FileDirective;
    /**
     * 是否通过头部注释引用。
     */
    readonly commentReference: boolean;
    /**
     * 引用语句的文本位置。
     */
    readonly span: TextSpan;
}

/**
 * 此类用于描述文件的解析分支
 */
export interface FileBranch {
    /**
     * 入口文件路径长度
     */
    readonly depth: number;
    /**
     * 此分支的入口文件
     */
    readonly entry: File;
    /**
     * 此分支的文件解析结果
     */
    readonly source: SourceFile | undefined;
}

/**
 * 此类管理文件对象的入口文件和互相引用关系
 */
export interface FileTracker {
    /**
     * 对应的文件
     */
    readonly file: File;
    /**
     * 当前文件的所有入口文件
     */
    readonly branches: readonly FileBranch[];
    /**
     * 当前文件的父文件，即通过#include引用此文件的的文件
     */
    readonly parents: readonly FileReference[];
    /**
     * 当前文件的子文件，即此文件通过#include引用的文件
     */
    readonly children: readonly FileReference[];
    /**
     * 将指定文件添加到父文件集合中
     * @param file
     * @param mark
     */
    asParent(file: File, directive: FileDirective | undefined, commentReference: boolean, span: TextSpan): void;
    /**
     * 将指定文件添加到子文件集合中
     * @param file
     * @param mark
     */
    asChild(file: File, directive: FileDirective | undefined, commentReference: boolean, span: TextSpan): void;
    /**
     * 获取指定路径的父文件对象，如果不存在，将返回`undefined`
     * @param path
     */
    getParent(path: string): FileReference | undefined;
    /**
     * 获取指定路径的子文件对象，如果不存在，将返回`undefined`
     * @param path
     */
    getChild(path: string): FileReference | undefined;
    /**
     * 判断指定文件是否是此文件的父文件
     * @param file
     */
    isParent(file: File): boolean;
    /**
     * 判断指定文件是否是此文件的子对象
     * @param file
     */
    isChild(file: File): boolean;
    /**
     * 移除指定路径的父文件，如果不存在，将跳过操作
     * @param path
     */
    removeParent(path: string): void;
    /**
     * 将指定文件移出父文件集合，如果此文件不在父文件集合中，将跳过操作
     * @param file
     */
    removeParent(file: File): void;
    /**
     * 将符合回调函数的父文件的对象移出父文件集合
     * @param func
     */
    removeParent(func: Predicate<File>): void;
    /**
     * 清空当前文件的父文件集合
     */
    clearParents(): void;
    /**
     * 移除指定路径的子文件，如果不存在，将跳过操作
     * @param path
     */
    removeChild(path: string): void;
    /**
     * 将指定文件移出子文件集合，如果此文件不在子文件集合中，将跳过操作
     * @param file
     */
    removeChild(file: File): void;
    /**
     * 将符合回调函数的子文件的对象移出子文件集合
     * @param func
     */
    removeChild(func: Predicate<File>): void;
    /**
     * 清空当前文件的子文件集合
     */
    clearChildren(): void;
    /**
     * 锁定指定入口文件的分支，锁定后，通过`getBranch`方法获取分支对象并忽略参数时，
     * 将优先返回已锁定的分支对象
     * @param entryPath
     */
    lockBranch(entryPath: string): FileBranch | undefined;
    /**
     * 添加新的文件路径分支，
     * 如果新配置的入口文件路径已存在，新的对象将替换掉已有的内容。
     * @param entry
     * @param relatives
     */
    asBranch(entry: File, depth: number): void;
    /**
     * 获取当前文件入口文件中路径深度最短的
     */
    getEntry(): File;
    /**
     * 获取指定路径的入口文件，如果不存在，返回`undefined`
     * @param path
     */
    getEntry(path: string): File | undefined;
    /**
     * 获取当前路径最短的路径分支
     */
    getBranch(): FileBranch;
    /**
     * 获取指定入口路径的路径分支
     * @param path
     */
    getBranch(path: string): FileBranch | undefined;
    /**
     * 更新指定入口路径分支的文件解析结果
     * @param entryPath
     * @param source
     */
    updateBranch(source: SourceFile, entryPath?: string): void;
    /**
     * 获取最近的解析结果或者路径最短的路径分支的解析结果，优先返回最近的结果
     */
    getSourceFile(): SourceFile | undefined;
    /**
     * 获取指定路径分支的源文件解析结果
     * @param entryPath
     */
    getSourceFile(entryPath: string): SourceFile | undefined;
}

export interface FileContentUpdateParam {
    text: string;
    changes: TextChange[];
}

/**
 * 此类表示源文件，包含文件名、文件内容、引用此文件的文件、此文件引用的文件、由Parser解析后的
 * 源文件对象、解析后的符号表等属性。
 */
export interface File {
    /**
     * 文件类型。
     */
    readonly kind: FileKind;
    /**
     * 此文件的完整路径，需注意，此属性值和URI不是相同的值。
     */
    readonly path: string;
    /**
     * 文件的文本内容。
     */
    readonly content: string;
    /**
     * 文件引用关系管理对象。
     */
    readonly tracker: FileTracker;
    /**
     * 当前文件的文本头部注释文件引用标记。
     */
    readonly directives: FileDirective[];
    /**
     * 当前文件的更新版本号。
     */
    readonly version: number;
    /**
     * 初始化文件引用关系
     */
    init(): void;
    /**
     * 更新文件的文本内容，并同步更新注释引用配置的文件引用关系。
     * @param param 更新后的文本内容
     */
    update(param: FileContentUpdateParam): void;
    /**
     * 获取当前文件所在的程序对象。
     */
    getProgram(): Program;
    /**
     * 此文件是否是入口文件
     */
    isEntry(): boolean;
    /**
     * 此文件是否是叶节点
     */
    isLeaf(): boolean;
    /**
     * 获取符合条件的父对象。
     * @param predicate
     */
    getParent(predicate: Predicate<File>): File | undefined;
    /**
     * 获取指定分支的源文件解析结果，如果不存在，将返回`undefined`
     * @param entryPath
     */
    getSourceFile(entryPath: string | undefined): SourceFile | undefined;
    /**
     * 更新指定分支的源文件解析结果，如果不指定文件路径，则默认更新最短路径的分支
     * @param sourceFile
     * @param entryPath
     */
    updateSourceFile(sourceFile: SourceFile, entryPath?: string): void;
    /**
     * 遍历子节点。
     * @param callback
     */
    traverse(callback: Func<FileReference, void>, stack?: File[]): void;
}

export interface Logger {
    (name: string): void;
}

export interface ProgramFileChangedHandler {
    (file: File): void;
}

/**
 * 此类用于存储和更新整个程序，包含文件夹内所有支持的文件。
 * 因为程序没有显式的函数或者模块引用，而是通过预处理的方式进行文本合并，
 * 所以，需要提前读取所有文件的相互引用关系，才能确定文件的解析顺序。
 */
export interface Program {
    /**
     * 当前程序的初始化状态。
     */
    readonly initialized: boolean;
    /**
     * 当前程序使用的全局元数据文件。
     */
    readonly localMetadata: string | undefined;
    /**
     * 获取当前程序的文件夹完整路径。
     */
    getCurrentDirectory(): string;
    /**
     * 更新当前的脚本配置。
     * @param options
     */
    updateScriptOptions(options: ScriptOptions): void;
    /**
     * 设置当前程序的日志记录回调函数。
     * @param handler 记录日志的回调函数
     */
    onLog(handler?: Logger): void;
    /**
     * 设置当前程序抛出错误时的回调函数。
     * @param handler 回调函数
     */
    onError(handler: Logger): void;
    /**
     * 当程序源文件发生改动时出发的事件。
     * @param handler
     */
    onProgramFileChanged(handler: ProgramFileChangedHandler): void;
    /**
     * 载入指定路径的文件内容：
     * + 当路径为文件夹时，将读取文件夹内的所有文件。
     * + 当路径为文件时，将读取文件所在文件夹的所有文件。
     * @param path 文件系统路径
     */
    load(path: string): void;
    /**
     * 读取指定路径的'.mdd'文件
     * @param path
     */
    loadLocalMetadataFile(path: string): void;
    /**
     * 解析指定路径的文本，在解析前，会寻找路径最短的解析入口文件。
     * @param fileName 文件的完整路径
     */
    parse(fileName: string): SourceFile | undefined;
    /**
     * 解析指定分支的文件，并更新路径中的所有文件。
     * @param branch
     */
    parse(branch: FileBranch): SourceFile | undefined;
    /**
     * 更新指定路径的文件内容。
     * @param fileName 文件完整路径
     * @param param 更新内容参数
     */
    update(fileName: string, param: FileContentUpdateParam): void;
    /**
     * 向当前文件集合中添加新的文件
     * + 可以不提供文件的内容，如果不提供，则默认读取指定文件的内容。
     * + 如果提供的文件路径已存在，则会使用新的文本内容替换已有的内容。
     * @param fileName 文件完整路径
     * @param content 文件的文本内容
     */
    addFile(fileName: string, content?: string): void;
    /**
     * 删除文件列表中的指定路径的文件对象，如果不存在，将忽略操作。
     * @param fileName 文件的完整路径
     */
    deleteFile(fileName: string): void;
    /**
     * 修改指定文件的文件名，并更新文件列表的关联信息。
     * @param oldName 修改前的名称
     * @param newName 修改后的名称
     */
    renameFile(oldName: string, newName: string): void;
    /**
     * 激活指定路径的文件，活动文件在调用`refresh`方法时会进行更新。
     * @param path
     */
    activateFile(path: string): void;
    /**
     * 将指定文件设置为不活动文件，更新配置时将不会同步更新文件解析结果。
     * @param path 指定文件路径
     */
    closeFile(path: string): void;
    /**
     * 更新当前活动文件的解析结果。
     */
    refresh(): void;
    /**
     * 判定指定路径的文件是否在当前的程序文件夹内。
     * @param fileName 文件完整路径
     */
    fileExists(fileName: string): boolean;
    /**
     * 获取指定路径的文件对象
     * @param fileName 文件完整路径
     */
    getFile(fileName: string): File | undefined;
    /**
     * 获取指定路径的源文件对象。
     * @param fileName 文件完整路径
     */
    getSourceFile(fileName: string, entryPath?: string): SourceFile | undefined;
    /**
     * 获取当前使用的TypeChecker实例化对象。
     */
    getTypeChecker(): TypeChecker
    /**
     * 清空已经读取的内容。
     */
    clear(): void;
    /**
     * 获取当前的本地声明符号表。
     */
    getLocalDeclarations(): ReadonlyMap<string, Symbol> | undefined;
    /**
     * 获取指定文件的相关文件路径，只会在激活文件中查找。
     * @param fileName
     */
    getRelatives(fileName: string): string[];
    /**
     * 获取指定文件的相关文件路径，并且可以使用回调函数返回自定义结果。
     * @param fileName
     * @param callback
     */
    getRelatives<T>(fileName: string, callback: (file: File) => T | undefined): T[];
}
